@eddyskywalker/dsh-chatgpt-subscription 0.26.0 → 0.27.0
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 +37 -0
- package/README.md +64 -0
- package/lib/client.js +2176 -116
- package/lib/client.js.map +1 -1
- package/lib/index.js +2211 -110
- package/lib/types/client/index.d.ts +2 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/progress-updates/contracts.d.ts +125 -0
- package/lib/types/client/progress-updates/contracts.d.ts.map +1 -0
- package/lib/types/client/progress-updates/guide-artwork.d.ts +28 -0
- package/lib/types/client/progress-updates/guide-artwork.d.ts.map +1 -0
- package/lib/types/client/progress-updates/host-source.d.ts +128 -0
- package/lib/types/client/progress-updates/host-source.d.ts.map +1 -0
- package/lib/types/client/progress-updates/index.d.ts +91 -0
- package/lib/types/client/progress-updates/index.d.ts.map +1 -0
- package/lib/types/client/progress-updates/locales.d.ts +127 -0
- package/lib/types/client/progress-updates/locales.d.ts.map +1 -0
- package/lib/types/client/progress-updates/markdown-lite.d.ts +60 -0
- package/lib/types/client/progress-updates/markdown-lite.d.ts.map +1 -0
- package/lib/types/client/progress-updates/progress-row.d.ts +87 -0
- package/lib/types/client/progress-updates/progress-row.d.ts.map +1 -0
- package/lib/types/client/progress-updates/settings-binding.d.ts +71 -0
- package/lib/types/client/progress-updates/settings-binding.d.ts.map +1 -0
- package/lib/types/client/progress-updates/settings-section.d.ts +57 -0
- package/lib/types/client/progress-updates/settings-section.d.ts.map +1 -0
- package/lib/types/client/progress-updates/styles.d.ts +34 -0
- package/lib/types/client/progress-updates/styles.d.ts.map +1 -0
- package/lib/types/client/progress-updates/tool-name.d.ts +14 -0
- package/lib/types/client/progress-updates/tool-name.d.ts.map +1 -0
- package/lib/types/client/progress-updates/updates-panel.d.ts +113 -0
- package/lib/types/client/progress-updates/updates-panel.d.ts.map +1 -0
- package/lib/types/client/step-code/locales.d.ts +2 -2
- package/lib/types/client/workbuddy/WorkBuddySection.d.ts.map +1 -1
- package/lib/types/client/workbuddy/locales.d.ts +108 -0
- package/lib/types/client/workbuddy/locales.d.ts.map +1 -1
- package/lib/types/client/workbuddy/styles.d.ts.map +1 -1
- package/lib/types/client/workbuddy/useWorkBuddySection.d.ts +9 -1
- package/lib/types/client/workbuddy/useWorkBuddySection.d.ts.map +1 -1
- package/lib/types/host/progress-updates/index.d.ts +90 -0
- package/lib/types/host/progress-updates/index.d.ts.map +1 -0
- package/lib/types/host/progress-updates/nudge.d.ts +91 -0
- package/lib/types/host/progress-updates/nudge.d.ts.map +1 -0
- package/lib/types/host/progress-updates/prompt.d.ts +34 -0
- package/lib/types/host/progress-updates/prompt.d.ts.map +1 -0
- package/lib/types/host/progress-updates/routes.d.ts +39 -0
- package/lib/types/host/progress-updates/routes.d.ts.map +1 -0
- package/lib/types/host/progress-updates/store.d.ts +74 -0
- package/lib/types/host/progress-updates/store.d.ts.map +1 -0
- package/lib/types/host/progress-updates/tool.d.ts +42 -0
- package/lib/types/host/progress-updates/tool.d.ts.map +1 -0
- package/lib/types/host/reasoning-collapse-guard/index.d.ts +5 -0
- package/lib/types/host/reasoning-collapse-guard/index.d.ts.map +1 -1
- package/lib/types/host/web-provider-reconciler.d.ts +34 -0
- package/lib/types/host/web-provider-reconciler.d.ts.map +1 -0
- package/lib/types/host/workbuddy/account-pool.d.ts +64 -1
- package/lib/types/host/workbuddy/account-pool.d.ts.map +1 -1
- package/lib/types/host/workbuddy/adapter.d.ts +32 -0
- package/lib/types/host/workbuddy/adapter.d.ts.map +1 -1
- package/lib/types/host/workbuddy/atrest.d.ts +98 -0
- package/lib/types/host/workbuddy/atrest.d.ts.map +1 -0
- package/lib/types/host/workbuddy/checkin.d.ts +16 -0
- package/lib/types/host/workbuddy/checkin.d.ts.map +1 -1
- package/lib/types/host/workbuddy/client.d.ts +9 -0
- package/lib/types/host/workbuddy/client.d.ts.map +1 -1
- package/lib/types/host/workbuddy/dsml.d.ts +55 -0
- package/lib/types/host/workbuddy/dsml.d.ts.map +1 -0
- package/lib/types/host/workbuddy/guardrails.d.ts +111 -0
- package/lib/types/host/workbuddy/guardrails.d.ts.map +1 -0
- package/lib/types/host/workbuddy/mapper.d.ts +11 -0
- package/lib/types/host/workbuddy/mapper.d.ts.map +1 -1
- package/lib/types/host/workbuddy/model-catalog.d.ts +8 -1
- package/lib/types/host/workbuddy/model-catalog.d.ts.map +1 -1
- package/lib/types/host/workbuddy/outbound-identity.d.ts +66 -0
- package/lib/types/host/workbuddy/outbound-identity.d.ts.map +1 -0
- package/lib/types/host/workbuddy/routes.d.ts +8 -1
- package/lib/types/host/workbuddy/routes.d.ts.map +1 -1
- package/lib/types/host/workbuddy/token-store.d.ts +93 -0
- package/lib/types/host/workbuddy/token-store.d.ts.map +1 -1
- package/lib/types/index.d.ts +63 -5
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/shared/workbuddy-contracts.d.ts +54 -2
- package/lib/types/shared/workbuddy-contracts.d.ts.map +1 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -2631,7 +2631,7 @@ window.__ModuleLoader__.load({
|
|
|
2631
2631
|
//#endregion
|
|
2632
2632
|
//#region src/client/antigravity/locales.ts
|
|
2633
2633
|
const NS_ANTIGRAVITY = "dsh-antigravity";
|
|
2634
|
-
const zh$
|
|
2634
|
+
const zh$10 = {
|
|
2635
2635
|
...accountPoolZh,
|
|
2636
2636
|
title: "Antigravity",
|
|
2637
2637
|
pageDesc: "登录 Google Antigravity / Cloud Code Assist,并查看当前账号的共享额度。",
|
|
@@ -2709,7 +2709,7 @@ window.__ModuleLoader__.load({
|
|
|
2709
2709
|
loginFailed: "登录失败",
|
|
2710
2710
|
tokens: "tokens"
|
|
2711
2711
|
};
|
|
2712
|
-
const en$
|
|
2712
|
+
const en$10 = {
|
|
2713
2713
|
...accountPoolEn,
|
|
2714
2714
|
title: "Antigravity",
|
|
2715
2715
|
pageDesc: "Sign in to Google Antigravity / Cloud Code Assist and view shared quotas.",
|
|
@@ -2787,11 +2787,11 @@ window.__ModuleLoader__.load({
|
|
|
2787
2787
|
loginFailed: "Login failed",
|
|
2788
2788
|
tokens: "tokens"
|
|
2789
2789
|
};
|
|
2790
|
-
const dictionaries$
|
|
2791
|
-
zh: zh$
|
|
2792
|
-
"zh-CN": zh$
|
|
2793
|
-
en: en$
|
|
2794
|
-
"en-US": en$
|
|
2790
|
+
const dictionaries$10 = {
|
|
2791
|
+
zh: zh$10,
|
|
2792
|
+
"zh-CN": zh$10,
|
|
2793
|
+
en: en$10,
|
|
2794
|
+
"en-US": en$10
|
|
2795
2795
|
};
|
|
2796
2796
|
//#endregion
|
|
2797
2797
|
//#region src/client/antigravity/AntigravitySection.tsx
|
|
@@ -2827,7 +2827,7 @@ window.__ModuleLoader__.load({
|
|
|
2827
2827
|
const [savingModel, setSavingModel] = (0, react.useState)(null);
|
|
2828
2828
|
/** Follow-up poll owed while the host refreshes the quota behind an answer. */
|
|
2829
2829
|
const quotaFollowUp = (0, react.useRef)(null);
|
|
2830
|
-
const t = zh$
|
|
2830
|
+
const t = zh$10;
|
|
2831
2831
|
const notifyChange = (0, react.useCallback)(() => {
|
|
2832
2832
|
onModelChange?.();
|
|
2833
2833
|
loadModelDirectory?.();
|
|
@@ -3384,7 +3384,7 @@ window.__ModuleLoader__.load({
|
|
|
3384
3384
|
//#endregion
|
|
3385
3385
|
//#region src/client/command-code/locales.ts
|
|
3386
3386
|
const NS_COMMAND_CODE = "dsh-command-code";
|
|
3387
|
-
const zh$
|
|
3387
|
+
const zh$9 = {
|
|
3388
3388
|
...accountPoolZh,
|
|
3389
3389
|
title: "Command Code",
|
|
3390
3390
|
pageDesc: "登录 Command Code,管理可用模型、上下文窗口与额度。",
|
|
@@ -3452,7 +3452,7 @@ window.__ModuleLoader__.load({
|
|
|
3452
3452
|
loginFailed: "登录失败",
|
|
3453
3453
|
tokens: "tokens"
|
|
3454
3454
|
};
|
|
3455
|
-
const en$
|
|
3455
|
+
const en$9 = {
|
|
3456
3456
|
...accountPoolEn,
|
|
3457
3457
|
title: "Command Code",
|
|
3458
3458
|
pageDesc: "Sign in to Command Code and manage models, context windows, and quota.",
|
|
@@ -3520,11 +3520,11 @@ window.__ModuleLoader__.load({
|
|
|
3520
3520
|
loginFailed: "Sign-in failed",
|
|
3521
3521
|
tokens: "tokens"
|
|
3522
3522
|
};
|
|
3523
|
-
const dictionaries$
|
|
3524
|
-
zh: zh$
|
|
3525
|
-
"zh-CN": zh$
|
|
3526
|
-
en: en$
|
|
3527
|
-
"en-US": en$
|
|
3523
|
+
const dictionaries$9 = {
|
|
3524
|
+
zh: zh$9,
|
|
3525
|
+
"zh-CN": zh$9,
|
|
3526
|
+
en: en$9,
|
|
3527
|
+
"en-US": en$9
|
|
3528
3528
|
};
|
|
3529
3529
|
//#endregion
|
|
3530
3530
|
//#region src/client/command-code/CommandCodeSection.tsx
|
|
@@ -3565,7 +3565,7 @@ window.__ModuleLoader__.load({
|
|
|
3565
3565
|
const [apiKey, setApiKey] = (0, react.useState)("");
|
|
3566
3566
|
/** Follow-up poll owed while the host refreshes the quota behind an answer. */
|
|
3567
3567
|
const quotaFollowUp = (0, react.useRef)(null);
|
|
3568
|
-
const t = zh$
|
|
3568
|
+
const t = zh$9;
|
|
3569
3569
|
const notifyChange = (0, react.useCallback)(() => {
|
|
3570
3570
|
onModelChange?.();
|
|
3571
3571
|
loadModelDirectory?.();
|
|
@@ -4146,7 +4146,7 @@ window.__ModuleLoader__.load({
|
|
|
4146
4146
|
//#endregion
|
|
4147
4147
|
//#region src/client/kimi-code/locales.ts
|
|
4148
4148
|
const NS_KIMI_CODE = "dsh-kimi-code";
|
|
4149
|
-
const zh$
|
|
4149
|
+
const zh$8 = {
|
|
4150
4150
|
...accountPoolZh,
|
|
4151
4151
|
title: "Kimi Code",
|
|
4152
4152
|
pageDesc: "登录 Kimi Code 订阅,管理模型、思考档位、上下文窗口与额度。",
|
|
@@ -4269,7 +4269,7 @@ window.__ModuleLoader__.load({
|
|
|
4269
4269
|
requests: "次请求",
|
|
4270
4270
|
tokens: "tokens"
|
|
4271
4271
|
};
|
|
4272
|
-
const en$
|
|
4272
|
+
const en$8 = {
|
|
4273
4273
|
...accountPoolEn,
|
|
4274
4274
|
title: "Kimi Code",
|
|
4275
4275
|
pageDesc: "Sign in to the Kimi Code subscription and manage models, thinking level, context windows, and quota.",
|
|
@@ -4392,11 +4392,11 @@ window.__ModuleLoader__.load({
|
|
|
4392
4392
|
requests: "requests",
|
|
4393
4393
|
tokens: "tokens"
|
|
4394
4394
|
};
|
|
4395
|
-
const dictionaries$
|
|
4396
|
-
zh: zh$
|
|
4397
|
-
"zh-CN": zh$
|
|
4398
|
-
en: en$
|
|
4399
|
-
"en-US": en$
|
|
4395
|
+
const dictionaries$8 = {
|
|
4396
|
+
zh: zh$8,
|
|
4397
|
+
"zh-CN": zh$8,
|
|
4398
|
+
en: en$8,
|
|
4399
|
+
"en-US": en$8
|
|
4400
4400
|
};
|
|
4401
4401
|
//#endregion
|
|
4402
4402
|
//#region src/client/kimi-code/KimiModelCapabilities.tsx
|
|
@@ -4409,7 +4409,7 @@ window.__ModuleLoader__.load({
|
|
|
4409
4409
|
* Capabilities are now short tags in their own columns, with the per-model
|
|
4410
4410
|
* detail kept in a hover title.
|
|
4411
4411
|
*/
|
|
4412
|
-
const t = zh$
|
|
4412
|
+
const t = zh$8;
|
|
4413
4413
|
/**
|
|
4414
4414
|
* Render one quota multiplier the way the official table states it: "2x", "3x".
|
|
4415
4415
|
*
|
|
@@ -4489,10 +4489,10 @@ window.__ModuleLoader__.load({
|
|
|
4489
4489
|
}
|
|
4490
4490
|
}
|
|
4491
4491
|
const EFFORT_LABEL = {
|
|
4492
|
-
low: zh$
|
|
4493
|
-
high: zh$
|
|
4494
|
-
max: zh$
|
|
4495
|
-
none: zh$
|
|
4492
|
+
low: zh$8.effortLow,
|
|
4493
|
+
high: zh$8.effortHigh,
|
|
4494
|
+
max: zh$8.effortMax,
|
|
4495
|
+
none: zh$8.effortNone
|
|
4496
4496
|
};
|
|
4497
4497
|
/** Human label for the last request's prefix-drift cause. */
|
|
4498
4498
|
function driftLabel(cause, t) {
|
|
@@ -4518,7 +4518,7 @@ window.__ModuleLoader__.load({
|
|
|
4518
4518
|
const [connectionNotice, setConnectionNotice] = (0, react.useState)(null);
|
|
4519
4519
|
/** Follow-up poll owed while the host refreshes the quota behind an answer. */
|
|
4520
4520
|
const quotaFollowUp = (0, react.useRef)(null);
|
|
4521
|
-
const t = zh$
|
|
4521
|
+
const t = zh$8;
|
|
4522
4522
|
const notifyChange = (0, react.useCallback)(() => {
|
|
4523
4523
|
onModelChange?.();
|
|
4524
4524
|
loadModelDirectory?.();
|
|
@@ -5260,8 +5260,17 @@ window.__ModuleLoader__.load({
|
|
|
5260
5260
|
}
|
|
5261
5261
|
//#endregion
|
|
5262
5262
|
//#region src/shared/workbuddy-contracts.ts
|
|
5263
|
-
/**
|
|
5263
|
+
/**
|
|
5264
|
+
* Every level this route can name, in escalating order (the card's sort key).
|
|
5265
|
+
*
|
|
5266
|
+
* `none` is the one level that is not a rung of any ladder: it is the measured
|
|
5267
|
+
* spelling that turns the reasoning trace OFF upstream (`reasoning_effort:
|
|
5268
|
+
* "none"`, the value the sibling gateway sends on its own cheap calls). It is
|
|
5269
|
+
* only ever offered on a model the catalog marks `canDisableThinking`, so it
|
|
5270
|
+
* sorts below everything and is never used to converge a level onto a ladder.
|
|
5271
|
+
*/
|
|
5264
5272
|
const WORKBUDDY_REASONING_EFFORTS = [
|
|
5273
|
+
"none",
|
|
5265
5274
|
"minimal",
|
|
5266
5275
|
"low",
|
|
5267
5276
|
"medium",
|
|
@@ -5272,7 +5281,7 @@ window.__ModuleLoader__.load({
|
|
|
5272
5281
|
//#endregion
|
|
5273
5282
|
//#region src/client/workbuddy/locales.ts
|
|
5274
5283
|
const NS_WORKBUDDY = "dsh-workbuddy";
|
|
5275
|
-
const zh$
|
|
5284
|
+
const zh$7 = {
|
|
5276
5285
|
title: "WorkBuddy",
|
|
5277
5286
|
pageDesc: "复用本机 CodeBuddy 桌面端登录态,管理 WorkBuddy(CodeBuddy 订阅)模型、上下文窗口与额度。",
|
|
5278
5287
|
account: "账号",
|
|
@@ -5356,9 +5365,27 @@ window.__ModuleLoader__.load({
|
|
|
5356
5365
|
checkinLastRun: "上次运行:{time}",
|
|
5357
5366
|
checkinNever: "尚未运行",
|
|
5358
5367
|
checkinNow: "立即签到",
|
|
5359
|
-
checkinRunning: "签到中…"
|
|
5368
|
+
checkinRunning: "签到中…",
|
|
5369
|
+
outboundSection: "出站身份",
|
|
5370
|
+
outboundHint: "决定请求以哪种客户端身份发出。cli 是本线路一直使用、实测可用的身份;auto 会按账号稳定分配一种(同一账号永不漂移)。换身份不会增加额度——额度按账号 × 模型的 24 小时窗口计算。",
|
|
5371
|
+
outboundAuto: "自动(按账号分配)",
|
|
5372
|
+
outboundCli: "CLI(默认,实测可用)",
|
|
5373
|
+
outboundWorkbuddy: "WorkBuddy 桌面端",
|
|
5374
|
+
outboundVscode: "VSCode 插件",
|
|
5375
|
+
atRestSection: "桌面凭据解密",
|
|
5376
|
+
atRestHint: "桌面客户端自 2026-09-24 起把 token 存成 $wbEncrypted 加密信封。粘贴一次密钥即可读取这些账号;密钥只写入系统加密存储(DPAPI / Keychain / Secret Service),不落明文、不进日志。",
|
|
5377
|
+
atRestPlaceholder: "64 位 hex 保护密钥,或 44 字符 base64 的 atRestSecretKey",
|
|
5378
|
+
atRestSave: "保存密钥",
|
|
5379
|
+
atRestClear: "清除密钥",
|
|
5380
|
+
atRestSaved: "已保存密钥",
|
|
5381
|
+
atRestSealed: "检测到 {count} 个加密凭据,未保存密钥前无法使用。",
|
|
5382
|
+
guardrailsSection: "本地额度护栏",
|
|
5383
|
+
guardrailsHint: "在撞上游 429 之前先停手:剩余积分低于阈值、账号当日 token 达额、或单个模型当日 token 达额时,请求会切到池里其他账号。三项都填 0 = 关闭,计数按本地 0 点重置。",
|
|
5384
|
+
guardrailsReserve: "保留积分",
|
|
5385
|
+
guardrailsDaily: "账号日 token",
|
|
5386
|
+
guardrailsModelDaily: "模型日 token"
|
|
5360
5387
|
};
|
|
5361
|
-
const en$
|
|
5388
|
+
const en$7 = {
|
|
5362
5389
|
title: "WorkBuddy",
|
|
5363
5390
|
pageDesc: "Reuse the local CodeBuddy desktop sign-in to manage WorkBuddy (CodeBuddy subscription) models, context windows, and quota.",
|
|
5364
5391
|
account: "Account",
|
|
@@ -5442,13 +5469,31 @@ window.__ModuleLoader__.load({
|
|
|
5442
5469
|
checkinLastRun: "Last run: {time}",
|
|
5443
5470
|
checkinNever: "Never run",
|
|
5444
5471
|
checkinNow: "Check in now",
|
|
5445
|
-
checkinRunning: "Checking in…"
|
|
5472
|
+
checkinRunning: "Checking in…",
|
|
5473
|
+
outboundSection: "Outbound identity",
|
|
5474
|
+
outboundHint: "Which client identity requests present. cli is what this route has always sent and was measured working; auto assigns one product per account and never drifts. Switching identity does not create allowance — quota is metered per account × model over a 24h window.",
|
|
5475
|
+
outboundAuto: "Auto (per account)",
|
|
5476
|
+
outboundCli: "CLI (default, measured working)",
|
|
5477
|
+
outboundWorkbuddy: "WorkBuddy desktop",
|
|
5478
|
+
outboundVscode: "VSCode extension",
|
|
5479
|
+
atRestSection: "Desktop credential decryption",
|
|
5480
|
+
atRestHint: "Since 2026-09-24 the desktop client seals its tokens as $wbEncrypted envelopes. Paste the key once to read those accounts; it is stored only in the platform credential store, never in plaintext and never logged.",
|
|
5481
|
+
atRestPlaceholder: "64-character hex protector key, or the 44-character base64 atRestSecretKey",
|
|
5482
|
+
atRestSave: "Save key",
|
|
5483
|
+
atRestClear: "Clear key",
|
|
5484
|
+
atRestSaved: "Key saved",
|
|
5485
|
+
atRestSealed: "{count} sealed credentials found; they stay unreadable until a key is saved.",
|
|
5486
|
+
guardrailsSection: "Local quota guardrails",
|
|
5487
|
+
guardrailsHint: "Stop before the upstream 429: below a credit floor, at the account daily token cap, or at a per-model daily token cap, the request moves to another account in the pool. 0 disables a rail; counters reset at local midnight.",
|
|
5488
|
+
guardrailsReserve: "Reserve credits",
|
|
5489
|
+
guardrailsDaily: "Account daily tokens",
|
|
5490
|
+
guardrailsModelDaily: "Model daily tokens"
|
|
5446
5491
|
};
|
|
5447
|
-
const dictionaries$
|
|
5448
|
-
zh: zh$
|
|
5449
|
-
"zh-CN": zh$
|
|
5450
|
-
en: en$
|
|
5451
|
-
"en-US": en$
|
|
5492
|
+
const dictionaries$7 = {
|
|
5493
|
+
zh: zh$7,
|
|
5494
|
+
"zh-CN": zh$7,
|
|
5495
|
+
en: en$7,
|
|
5496
|
+
"en-US": en$7
|
|
5452
5497
|
};
|
|
5453
5498
|
//#endregion
|
|
5454
5499
|
//#region src/client/workbuddy/useWorkBuddySection.ts
|
|
@@ -5541,7 +5586,7 @@ window.__ModuleLoader__.load({
|
|
|
5541
5586
|
const quotaFollowUp = (0, react.useRef)(null);
|
|
5542
5587
|
const [contextDrafts, setContextDrafts] = (0, react.useState)({});
|
|
5543
5588
|
const [savingModel, setSavingModel] = (0, react.useState)(null);
|
|
5544
|
-
const t = zh$
|
|
5589
|
+
const t = zh$7;
|
|
5545
5590
|
const notifyChange = (0, react.useCallback)(() => {
|
|
5546
5591
|
onModelChange?.();
|
|
5547
5592
|
loadModelDirectory?.();
|
|
@@ -5743,6 +5788,67 @@ window.__ModuleLoader__.load({
|
|
|
5743
5788
|
setBusy(null);
|
|
5744
5789
|
}
|
|
5745
5790
|
};
|
|
5791
|
+
/** Switch which client identity chat requests present. */
|
|
5792
|
+
const updateOutboundIdentity = async (outboundIdentity) => {
|
|
5793
|
+
try {
|
|
5794
|
+
setBusy("outbound");
|
|
5795
|
+
setError(null);
|
|
5796
|
+
const updated = await api$10.request("/settings", {
|
|
5797
|
+
method: "POST",
|
|
5798
|
+
body: JSON.stringify({ outboundIdentity })
|
|
5799
|
+
});
|
|
5800
|
+
setStatus(normalizeStatus(updated));
|
|
5801
|
+
} catch (err) {
|
|
5802
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
5803
|
+
} finally {
|
|
5804
|
+
setBusy(null);
|
|
5805
|
+
}
|
|
5806
|
+
};
|
|
5807
|
+
/** Arm, change or clear the local quota guardrails. */
|
|
5808
|
+
const updateGuardrails = async (guardrails) => {
|
|
5809
|
+
try {
|
|
5810
|
+
setBusy("guardrails");
|
|
5811
|
+
setError(null);
|
|
5812
|
+
const updated = await api$10.request("/settings", {
|
|
5813
|
+
method: "POST",
|
|
5814
|
+
body: JSON.stringify({ guardrails })
|
|
5815
|
+
});
|
|
5816
|
+
setStatus(normalizeStatus(updated));
|
|
5817
|
+
} catch (err) {
|
|
5818
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
5819
|
+
} finally {
|
|
5820
|
+
setBusy(null);
|
|
5821
|
+
}
|
|
5822
|
+
};
|
|
5823
|
+
/** Store the desktop client's at-rest key, which unlocks sealed credentials. */
|
|
5824
|
+
const saveAtRestKey = async (key) => {
|
|
5825
|
+
try {
|
|
5826
|
+
setBusy("atrest");
|
|
5827
|
+
setError(null);
|
|
5828
|
+
const updated = await api$10.request("/at-rest-key", {
|
|
5829
|
+
method: "POST",
|
|
5830
|
+
body: JSON.stringify({ key })
|
|
5831
|
+
});
|
|
5832
|
+
setStatus(normalizeStatus(updated));
|
|
5833
|
+
} catch (err) {
|
|
5834
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
5835
|
+
} finally {
|
|
5836
|
+
setBusy(null);
|
|
5837
|
+
}
|
|
5838
|
+
};
|
|
5839
|
+
/** Forget the at-rest key; sealed accounts become unreadable again. */
|
|
5840
|
+
const clearAtRestKey = async () => {
|
|
5841
|
+
try {
|
|
5842
|
+
setBusy("atrest");
|
|
5843
|
+
setError(null);
|
|
5844
|
+
const updated = await api$10.request("/at-rest-key", { method: "DELETE" });
|
|
5845
|
+
setStatus(normalizeStatus(updated));
|
|
5846
|
+
} catch (err) {
|
|
5847
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
5848
|
+
} finally {
|
|
5849
|
+
setBusy(null);
|
|
5850
|
+
}
|
|
5851
|
+
};
|
|
5746
5852
|
const toggleModel = async (modelId, enabled) => {
|
|
5747
5853
|
if (status === null) return;
|
|
5748
5854
|
const current = status.models.filter((model) => model.enabled).map((model) => model.id);
|
|
@@ -5932,7 +6038,11 @@ window.__ModuleLoader__.load({
|
|
|
5932
6038
|
saveContextWindow,
|
|
5933
6039
|
resetContextWindow,
|
|
5934
6040
|
resetAllContextWindows,
|
|
5935
|
-
updateContextDraft
|
|
6041
|
+
updateContextDraft,
|
|
6042
|
+
updateOutboundIdentity,
|
|
6043
|
+
updateGuardrails,
|
|
6044
|
+
saveAtRestKey,
|
|
6045
|
+
clearAtRestKey
|
|
5936
6046
|
}
|
|
5937
6047
|
};
|
|
5938
6048
|
}
|
|
@@ -5951,6 +6061,7 @@ window.__ModuleLoader__.load({
|
|
|
5951
6061
|
* string values, so this nested table lives with the control that renders it.
|
|
5952
6062
|
*/
|
|
5953
6063
|
const EFFORT_LABELS$2 = {
|
|
6064
|
+
none: "Off",
|
|
5954
6065
|
minimal: "Minimal",
|
|
5955
6066
|
low: "Low",
|
|
5956
6067
|
medium: "Medium",
|
|
@@ -6016,8 +6127,11 @@ window.__ModuleLoader__.load({
|
|
|
6016
6127
|
});
|
|
6017
6128
|
const { status, loading, busy, error, connection, loginProgress, contextDrafts, savingModel } = state;
|
|
6018
6129
|
const { poolAccounts, effortChoices, allModels, strayEffort, contextModels, overrideCount, quota, authenticated, unreachable, poolLabels } = derived;
|
|
6019
|
-
const
|
|
6020
|
-
const
|
|
6130
|
+
const sealedCount = status?.sealedAccounts ?? 0;
|
|
6131
|
+
const { login, rescan, accountAction, deleteAccount, setStrategy, testConnection, refreshQuota, checkinNow, updateCheckin, toggleModel, setAllModels, updateEffort, refreshCatalog, saveContextWindow, resetContextWindow, resetAllContextWindows, updateContextDraft, updateOutboundIdentity, saveAtRestKey, clearAtRestKey, updateGuardrails } = actions;
|
|
6132
|
+
const [keyDraft, setKeyDraft] = (0, react.useState)("");
|
|
6133
|
+
const [guardrailDrafts, setGuardrailDrafts] = (0, react.useState)({});
|
|
6134
|
+
const t = zh$7;
|
|
6021
6135
|
if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6022
6136
|
className: "dsha-page",
|
|
6023
6137
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -6112,6 +6226,142 @@ window.__ModuleLoader__.load({
|
|
|
6112
6226
|
})
|
|
6113
6227
|
]
|
|
6114
6228
|
}),
|
|
6229
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
6230
|
+
className: "dsha-group",
|
|
6231
|
+
children: [
|
|
6232
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6233
|
+
className: "dsha-label",
|
|
6234
|
+
style: { fontWeight: 600 },
|
|
6235
|
+
children: t.outboundSection
|
|
6236
|
+
}),
|
|
6237
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6238
|
+
className: "dsha-muted",
|
|
6239
|
+
children: t.outboundHint
|
|
6240
|
+
}),
|
|
6241
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6242
|
+
style: { margin: "10px 0 14px" },
|
|
6243
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
6244
|
+
className: "dsha-select",
|
|
6245
|
+
value: status?.outboundIdentity ?? "cli",
|
|
6246
|
+
disabled: busy !== null,
|
|
6247
|
+
onChange: (event) => void updateOutboundIdentity(event.currentTarget.value),
|
|
6248
|
+
children: [
|
|
6249
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6250
|
+
value: "cli",
|
|
6251
|
+
children: t.outboundCli
|
|
6252
|
+
}),
|
|
6253
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6254
|
+
value: "auto",
|
|
6255
|
+
children: t.outboundAuto
|
|
6256
|
+
}),
|
|
6257
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6258
|
+
value: "workbuddy",
|
|
6259
|
+
children: t.outboundWorkbuddy
|
|
6260
|
+
}),
|
|
6261
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
6262
|
+
value: "vscode",
|
|
6263
|
+
children: t.outboundVscode
|
|
6264
|
+
})
|
|
6265
|
+
]
|
|
6266
|
+
})
|
|
6267
|
+
}),
|
|
6268
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6269
|
+
className: "dsha-label",
|
|
6270
|
+
style: {
|
|
6271
|
+
fontWeight: 600,
|
|
6272
|
+
marginTop: 12
|
|
6273
|
+
},
|
|
6274
|
+
children: t.guardrailsSection
|
|
6275
|
+
}),
|
|
6276
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6277
|
+
className: "dsha-muted",
|
|
6278
|
+
children: t.guardrailsHint
|
|
6279
|
+
}),
|
|
6280
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
6281
|
+
className: "dshwb-fields",
|
|
6282
|
+
children: [
|
|
6283
|
+
["reserveCredits", t.guardrailsReserve],
|
|
6284
|
+
["dailyTokens", t.guardrailsDaily],
|
|
6285
|
+
["modelDailyTokens", t.guardrailsModelDaily]
|
|
6286
|
+
].map(([field, label]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
6287
|
+
className: "dshwb-field",
|
|
6288
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: label }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
6289
|
+
className: "dshwb-control",
|
|
6290
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6291
|
+
type: "number",
|
|
6292
|
+
min: 0,
|
|
6293
|
+
step: 1,
|
|
6294
|
+
disabled: busy !== null,
|
|
6295
|
+
value: guardrailDrafts[field] ?? String(status?.guardrails?.[field] ?? 0),
|
|
6296
|
+
onChange: (event) => setGuardrailDrafts((prev) => ({
|
|
6297
|
+
...prev,
|
|
6298
|
+
[field]: event.currentTarget.value
|
|
6299
|
+
})),
|
|
6300
|
+
onBlur: () => {
|
|
6301
|
+
const draft = guardrailDrafts[field];
|
|
6302
|
+
if (draft === void 0) return;
|
|
6303
|
+
setGuardrailDrafts((prev) => {
|
|
6304
|
+
const next = { ...prev };
|
|
6305
|
+
delete next[field];
|
|
6306
|
+
return next;
|
|
6307
|
+
});
|
|
6308
|
+
const value = Math.floor(Number(draft));
|
|
6309
|
+
if (!Number.isFinite(value) || value < 0) return;
|
|
6310
|
+
updateGuardrails({ [field]: value });
|
|
6311
|
+
}
|
|
6312
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", { children: field === "reserveCredits" ? "credits" : "tokens" })]
|
|
6313
|
+
})]
|
|
6314
|
+
}, field))
|
|
6315
|
+
}),
|
|
6316
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6317
|
+
className: "dsha-label",
|
|
6318
|
+
style: {
|
|
6319
|
+
fontWeight: 600,
|
|
6320
|
+
marginTop: 12
|
|
6321
|
+
},
|
|
6322
|
+
children: t.atRestSection
|
|
6323
|
+
}),
|
|
6324
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6325
|
+
className: "dsha-muted",
|
|
6326
|
+
children: t.atRestHint
|
|
6327
|
+
}),
|
|
6328
|
+
sealedCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6329
|
+
className: "dsha-notice",
|
|
6330
|
+
children: t.atRestSealed.replace("{count}", String(sealedCount))
|
|
6331
|
+
}) : null,
|
|
6332
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6333
|
+
className: "dshwb-inputrow",
|
|
6334
|
+
children: [
|
|
6335
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
6336
|
+
type: "password",
|
|
6337
|
+
autoComplete: "off",
|
|
6338
|
+
spellCheck: false,
|
|
6339
|
+
placeholder: t.atRestPlaceholder,
|
|
6340
|
+
value: keyDraft,
|
|
6341
|
+
disabled: busy !== null,
|
|
6342
|
+
onChange: (event) => setKeyDraft(event.currentTarget.value)
|
|
6343
|
+
}),
|
|
6344
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6345
|
+
className: "dsha-btn dsha-btn-primary",
|
|
6346
|
+
disabled: busy !== null || keyDraft.trim() === "",
|
|
6347
|
+
onClick: () => {
|
|
6348
|
+
saveAtRestKey(keyDraft.trim()).then(() => setKeyDraft(""));
|
|
6349
|
+
},
|
|
6350
|
+
children: busy === "atrest" ? t.saving : t.atRestSave
|
|
6351
|
+
}),
|
|
6352
|
+
status?.atRestKeySet === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
6353
|
+
className: "dsha-muted",
|
|
6354
|
+
children: t.atRestSaved
|
|
6355
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6356
|
+
className: "dsha-btn",
|
|
6357
|
+
disabled: busy !== null,
|
|
6358
|
+
onClick: () => void clearAtRestKey(),
|
|
6359
|
+
children: t.atRestClear
|
|
6360
|
+
})] }) : null
|
|
6361
|
+
]
|
|
6362
|
+
})
|
|
6363
|
+
]
|
|
6364
|
+
}),
|
|
6115
6365
|
status?.checkin != null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
6116
6366
|
className: "dsha-group",
|
|
6117
6367
|
children: [
|
|
@@ -6464,7 +6714,7 @@ window.__ModuleLoader__.load({
|
|
|
6464
6714
|
//#endregion
|
|
6465
6715
|
//#region src/client/minimax-code/locales.ts
|
|
6466
6716
|
const NS_MINIMAX_CODE = "dsh-minimax-code";
|
|
6467
|
-
const zh$
|
|
6717
|
+
const zh$6 = {
|
|
6468
6718
|
...accountPoolZh,
|
|
6469
6719
|
title: "MiniMax Code",
|
|
6470
6720
|
pageDesc: "登录 MiniMax Code 编程订阅(设备码 OAuth),复用本机 MiniMax Code 的登录态,管理账号、连接与模型。",
|
|
@@ -6574,7 +6824,7 @@ window.__ModuleLoader__.load({
|
|
|
6574
6824
|
loading: "加载中...",
|
|
6575
6825
|
unknown: "—"
|
|
6576
6826
|
};
|
|
6577
|
-
const en$
|
|
6827
|
+
const en$6 = {
|
|
6578
6828
|
...accountPoolEn,
|
|
6579
6829
|
title: "MiniMax Code",
|
|
6580
6830
|
pageDesc: "Sign in to the MiniMax Code coding subscription (device-code OAuth) and reuse the local MiniMax Code sign-in to manage the account, connection, and models.",
|
|
@@ -6684,11 +6934,11 @@ window.__ModuleLoader__.load({
|
|
|
6684
6934
|
loading: "Loading...",
|
|
6685
6935
|
unknown: "—"
|
|
6686
6936
|
};
|
|
6687
|
-
const dictionaries$
|
|
6688
|
-
zh: zh$
|
|
6689
|
-
"zh-CN": zh$
|
|
6690
|
-
en: en$
|
|
6691
|
-
"en-US": en$
|
|
6937
|
+
const dictionaries$6 = {
|
|
6938
|
+
zh: zh$6,
|
|
6939
|
+
"zh-CN": zh$6,
|
|
6940
|
+
en: en$6,
|
|
6941
|
+
"en-US": en$6
|
|
6692
6942
|
};
|
|
6693
6943
|
//#endregion
|
|
6694
6944
|
//#region src/client/minimax-code/useMinimaxCodeSection.ts
|
|
@@ -6702,7 +6952,7 @@ window.__ModuleLoader__.load({
|
|
|
6702
6952
|
* rather than inferred from the JSX.
|
|
6703
6953
|
*/
|
|
6704
6954
|
/** The card's own dictionary; stable identity keeps effect dependencies intact. */
|
|
6705
|
-
const fallbackTranslate$
|
|
6955
|
+
const fallbackTranslate$3 = (key) => zh$6[key];
|
|
6706
6956
|
/**
|
|
6707
6957
|
* The shared pool card's labels, resolved through this card's dictionary.
|
|
6708
6958
|
*
|
|
@@ -6795,7 +7045,7 @@ window.__ModuleLoader__.load({
|
|
|
6795
7045
|
/** Device-code poll cadence. The host owns the real expiry. */
|
|
6796
7046
|
const LOGIN_POLL_INTERVAL_MS$2 = 2e3;
|
|
6797
7047
|
function useMinimaxCodeSection({ onModelChange }) {
|
|
6798
|
-
const t = fallbackTranslate$
|
|
7048
|
+
const t = fallbackTranslate$3;
|
|
6799
7049
|
const [status, setStatus] = (0, react.useState)(null);
|
|
6800
7050
|
const [loading, setLoading] = (0, react.useState)(true);
|
|
6801
7051
|
const [busy, setBusy] = (0, react.useState)(null);
|
|
@@ -7292,7 +7542,7 @@ window.__ModuleLoader__.load({
|
|
|
7292
7542
|
const { status, loading, busy, error, flow, copied, connectionNotice, contextDrafts, savingModel, pool, resetCards, resetNotice, selectedResetCardId } = state;
|
|
7293
7543
|
const { authenticated, account, quota, models, contextModels, overrideCount, poolLabels, resetCardList, resetCardFlows } = derived;
|
|
7294
7544
|
const { login, cancelLogin, copyCode, logout, poolAction, setStrategy, toggleModel, setAllModels, setCheckinEnabled, runCheckinNow, updateEffort, saveContextWindow, resetContextWindow, resetAllContextWindows, refreshQuota, refreshResetCards, selectResetCard, useResetCard, testConnection, updateContextDraft } = actions;
|
|
7295
|
-
const t = fallbackTranslate$
|
|
7545
|
+
const t = fallbackTranslate$3;
|
|
7296
7546
|
if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
7297
7547
|
className: "dsha-page",
|
|
7298
7548
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -7784,7 +8034,7 @@ window.__ModuleLoader__.load({
|
|
|
7784
8034
|
* 键名重合的会被渲染成 ChatGPT 的措辞,不重合的会把键名本身打印出来)。
|
|
7785
8035
|
*/
|
|
7786
8036
|
const NS_GLM_CODING_PLAN = "dsh-glm-coding-plan";
|
|
7787
|
-
const zh$
|
|
8037
|
+
const zh$5 = {
|
|
7788
8038
|
...accountPoolZh,
|
|
7789
8039
|
title: "GLM(ZCode 账号)",
|
|
7790
8040
|
pageDesc: "用 ZCode 账号授权接入 GLM Coding Plan(国区 bigmodel.cn / 国际区 z.ai),管理账号池、套餐额度与模型勾选。两个区域的凭据各自独立,池内可以同时存在两区账号。",
|
|
@@ -7878,7 +8128,7 @@ window.__ModuleLoader__.load({
|
|
|
7878
8128
|
quotaAllowanceUnknown: "未判定",
|
|
7879
8129
|
quotaAllowanceHint: "登录时探活认定的额度来源。「免费体验额度」指新用户注册后自动获得、首次使用起 5 天内每天 800 万 token 的体验额度。"
|
|
7880
8130
|
};
|
|
7881
|
-
const en$
|
|
8131
|
+
const en$5 = {
|
|
7882
8132
|
...accountPoolEn,
|
|
7883
8133
|
title: "GLM (ZCode account)",
|
|
7884
8134
|
pageDesc: "Sign in with a ZCode account to reach GLM Coding Plan (China bigmodel.cn / International z.ai), and manage the account pool, plan quota and model selection. The two regions hold independent credentials, so a pool can carry accounts from both.",
|
|
@@ -7972,11 +8222,11 @@ window.__ModuleLoader__.load({
|
|
|
7972
8222
|
quotaAllowanceUnknown: "Undetermined",
|
|
7973
8223
|
quotaAllowanceHint: "The allowance source established by the sign-in probe. “Free trial allowance” means 8M tokens per day for 5 days from first use, granted automatically to new accounts."
|
|
7974
8224
|
};
|
|
7975
|
-
const dictionaries$
|
|
7976
|
-
zh: zh$
|
|
7977
|
-
"zh-CN": zh$
|
|
7978
|
-
en: en$
|
|
7979
|
-
"en-US": en$
|
|
8225
|
+
const dictionaries$5 = {
|
|
8226
|
+
zh: zh$5,
|
|
8227
|
+
"zh-CN": zh$5,
|
|
8228
|
+
en: en$5,
|
|
8229
|
+
"en-US": en$5
|
|
7980
8230
|
};
|
|
7981
8231
|
/** 一个配额窗口的键到「窗口长度(分钟)」的映射。 */
|
|
7982
8232
|
const QUOTA_WINDOW_MINUTES$1 = {
|
|
@@ -8057,7 +8307,7 @@ window.__ModuleLoader__.load({
|
|
|
8057
8307
|
/** 登录状态机的轮询节奏。真实超时与间隔由 host 拥有,卡片只是跟随。 */
|
|
8058
8308
|
const LOGIN_POLL_INTERVAL_MS$1 = 2e3;
|
|
8059
8309
|
/** 本卡片的字典;身份稳定,依赖它的 effect 才不会每次渲染重建。 */
|
|
8060
|
-
const fallbackTranslate$
|
|
8310
|
+
const fallbackTranslate$2 = (key) => zh$5[key];
|
|
8061
8311
|
/**
|
|
8062
8312
|
* 共享账号池卡片需要的标签集,按本卡片的字典逐键查出来。
|
|
8063
8313
|
*
|
|
@@ -8119,7 +8369,7 @@ window.__ModuleLoader__.load({
|
|
|
8119
8369
|
});
|
|
8120
8370
|
}
|
|
8121
8371
|
function useGlmCodingPlanSection({ onModelChange } = {}) {
|
|
8122
|
-
const t = fallbackTranslate$
|
|
8372
|
+
const t = fallbackTranslate$2;
|
|
8123
8373
|
const [status, setStatus] = (0, react.useState)(null);
|
|
8124
8374
|
const [loading, setLoading] = (0, react.useState)(true);
|
|
8125
8375
|
const [busy, setBusy] = (0, react.useState)(null);
|
|
@@ -8474,14 +8724,14 @@ window.__ModuleLoader__.load({
|
|
|
8474
8724
|
*/
|
|
8475
8725
|
/** 区域的人类可读名,登录与 adopt 两条入口的结果都要说清楚落在哪一区。 */
|
|
8476
8726
|
function regionLabel$1(region) {
|
|
8477
|
-
return region === "cn" ? zh$
|
|
8727
|
+
return region === "cn" ? zh$5.regionCn : zh$5.regionIntl;
|
|
8478
8728
|
}
|
|
8479
8729
|
function GlmCodingPlanSection({ onModelChange } = {}) {
|
|
8480
8730
|
const { state, derived, actions } = useGlmCodingPlanSection({ onModelChange });
|
|
8481
8731
|
const { status, loading, busy, error, connection, adoptResult, pasteValue, region, pasteOpen, contextDrafts, savingModel } = state;
|
|
8482
8732
|
const { accounts, models, contextModels, overrideCount, login, quota, authenticated, poolLabels } = derived;
|
|
8483
8733
|
const { setRegion, login: startLogin, cancelLogin, submitPaste, setPasteValue, togglePaste, adopt, clearAdoptResult, accountAction, setStrategy, toggleModel, setAllModels, saveContextWindow, resetContextWindow, resetAllContextWindows, updateContextDraft, refreshQuota, refreshCatalog, testConnection } = actions;
|
|
8484
|
-
const t = zh$
|
|
8734
|
+
const t = zh$5;
|
|
8485
8735
|
if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8486
8736
|
className: "dsha-page",
|
|
8487
8737
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -8946,7 +9196,7 @@ window.__ModuleLoader__.load({
|
|
|
8946
9196
|
* provider — the same reason every sibling line has one.
|
|
8947
9197
|
*/
|
|
8948
9198
|
const NS_CLAUDE = "dsh-claude";
|
|
8949
|
-
const zh$
|
|
9199
|
+
const zh$4 = {
|
|
8950
9200
|
...accountPoolZh,
|
|
8951
9201
|
title: "Claude(订阅)",
|
|
8952
9202
|
pageDesc: "登录 Claude Pro/Max 订阅,管理模型、思考档位、上下文窗口与额度。",
|
|
@@ -9063,7 +9313,7 @@ window.__ModuleLoader__.load({
|
|
|
9063
9313
|
copy: "复制",
|
|
9064
9314
|
copied: "已复制"
|
|
9065
9315
|
};
|
|
9066
|
-
const en$
|
|
9316
|
+
const en$4 = {
|
|
9067
9317
|
...accountPoolEn,
|
|
9068
9318
|
title: "Claude (subscription)",
|
|
9069
9319
|
pageDesc: "Sign in to a Claude Pro/Max subscription and manage models, thinking levels, context windows and quota.",
|
|
@@ -9180,11 +9430,11 @@ window.__ModuleLoader__.load({
|
|
|
9180
9430
|
copy: "Copy",
|
|
9181
9431
|
copied: "Copied"
|
|
9182
9432
|
};
|
|
9183
|
-
const dictionaries$
|
|
9184
|
-
zh: zh$
|
|
9185
|
-
"zh-CN": zh$
|
|
9186
|
-
en: en$
|
|
9187
|
-
"en-US": en$
|
|
9433
|
+
const dictionaries$4 = {
|
|
9434
|
+
zh: zh$4,
|
|
9435
|
+
"zh-CN": zh$4,
|
|
9436
|
+
en: en$4,
|
|
9437
|
+
"en-US": en$4
|
|
9188
9438
|
};
|
|
9189
9439
|
//#endregion
|
|
9190
9440
|
//#region src/client/claude/useClaudeSection.ts
|
|
@@ -9218,7 +9468,7 @@ window.__ModuleLoader__.load({
|
|
|
9218
9468
|
const [connection, setConnection] = (0, react.useState)(null);
|
|
9219
9469
|
/** Follow-up poll owed while the host refreshes the quota behind an answer. */
|
|
9220
9470
|
const quotaFollowUp = (0, react.useRef)(null);
|
|
9221
|
-
const t = zh$
|
|
9471
|
+
const t = zh$4;
|
|
9222
9472
|
const notifyChange = (0, react.useCallback)(() => {
|
|
9223
9473
|
onModelChange?.();
|
|
9224
9474
|
loadModelDirectory?.();
|
|
@@ -9665,11 +9915,11 @@ window.__ModuleLoader__.load({
|
|
|
9665
9915
|
* out here would hide a choice the host would accept and a catalog may expose.
|
|
9666
9916
|
*/
|
|
9667
9917
|
const EFFORT_LABELS$1 = {
|
|
9668
|
-
low: zh$
|
|
9669
|
-
medium: zh$
|
|
9670
|
-
high: zh$
|
|
9671
|
-
xhigh: zh$
|
|
9672
|
-
max: zh$
|
|
9918
|
+
low: zh$4.effortLow,
|
|
9919
|
+
medium: zh$4.effortMedium,
|
|
9920
|
+
high: zh$4.effortHigh,
|
|
9921
|
+
xhigh: zh$4.effortXHigh,
|
|
9922
|
+
max: zh$4.effortMax
|
|
9673
9923
|
};
|
|
9674
9924
|
/** One-line capability summary shown as a model pill's hover title. */
|
|
9675
9925
|
function modelFacts(model, t) {
|
|
@@ -9690,7 +9940,7 @@ window.__ModuleLoader__.load({
|
|
|
9690
9940
|
const { status, loading, busy, error, flow, connection, pasteValue, contextDrafts, savingModel } = state;
|
|
9691
9941
|
const { accounts, hasAdopted, contextModels, overrideCount, quota, windows, poolLabels } = derived;
|
|
9692
9942
|
const { login, cancelLogin, submitPaste, adopt, stopImporting, accountAction, setStrategy, relogin, refreshQuota, refreshCatalog, testConnection, toggleModel, setAllModels, updateEffort, updateCacheTtl, saveContextWindow, resetContextWindow, resetAllContextWindows, updateContextDraft, setPasteValue } = actions;
|
|
9693
|
-
const t = zh$
|
|
9943
|
+
const t = zh$4;
|
|
9694
9944
|
if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9695
9945
|
className: "dsha-page",
|
|
9696
9946
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -10281,7 +10531,7 @@ window.__ModuleLoader__.load({
|
|
|
10281
10531
|
//#endregion
|
|
10282
10532
|
//#region src/client/ollama/locales.ts
|
|
10283
10533
|
const NS_OLLAMA = "dsh-ollama";
|
|
10284
|
-
const zh$
|
|
10534
|
+
const zh$3 = {
|
|
10285
10535
|
...accountPoolZh,
|
|
10286
10536
|
relogin: "更换 Key",
|
|
10287
10537
|
needsRelogin: "Key 已失效",
|
|
@@ -10333,7 +10583,7 @@ window.__ModuleLoader__.load({
|
|
|
10333
10583
|
usageNone: "尚无请求",
|
|
10334
10584
|
usageHint: "按 Ollama 每次响应自带的 token 计数本地累加。"
|
|
10335
10585
|
};
|
|
10336
|
-
const en$
|
|
10586
|
+
const en$3 = {
|
|
10337
10587
|
...accountPoolEn,
|
|
10338
10588
|
relogin: "Replace Key",
|
|
10339
10589
|
needsRelogin: "Key rejected",
|
|
@@ -10385,11 +10635,11 @@ window.__ModuleLoader__.load({
|
|
|
10385
10635
|
usageNone: "No requests yet",
|
|
10386
10636
|
usageHint: "Summed locally from the token counts Ollama returns on each response."
|
|
10387
10637
|
};
|
|
10388
|
-
const dictionaries$
|
|
10389
|
-
zh: zh$
|
|
10390
|
-
"zh-CN": zh$
|
|
10391
|
-
en: en$
|
|
10392
|
-
"en-US": en$
|
|
10638
|
+
const dictionaries$3 = {
|
|
10639
|
+
zh: zh$3,
|
|
10640
|
+
"zh-CN": zh$3,
|
|
10641
|
+
en: en$3,
|
|
10642
|
+
"en-US": en$3
|
|
10393
10643
|
};
|
|
10394
10644
|
//#endregion
|
|
10395
10645
|
//#region src/client/ollama/OllamaSection.tsx
|
|
@@ -10515,7 +10765,7 @@ window.__ModuleLoader__.load({
|
|
|
10515
10765
|
const setEffort = (0, react.useCallback)((value) => {
|
|
10516
10766
|
run("effort", () => API.setDefaultReasoningEffort(value === "" ? null : value));
|
|
10517
10767
|
}, [run]);
|
|
10518
|
-
const t = zh$
|
|
10768
|
+
const t = zh$3;
|
|
10519
10769
|
const pool = status?.pool;
|
|
10520
10770
|
const models = status?.models ?? [];
|
|
10521
10771
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
|
|
@@ -10630,7 +10880,7 @@ window.__ModuleLoader__.load({
|
|
|
10630
10880
|
className: "dsha-btn",
|
|
10631
10881
|
disabled: busy !== null,
|
|
10632
10882
|
onClick: () => setShowKeyForm(false),
|
|
10633
|
-
children: zh$
|
|
10883
|
+
children: zh$3.deleteAccount
|
|
10634
10884
|
})]
|
|
10635
10885
|
})
|
|
10636
10886
|
]
|
|
@@ -10750,7 +11000,7 @@ window.__ModuleLoader__.load({
|
|
|
10750
11000
|
* 键名重合的会被渲染成 ChatGPT 的措辞,不重合的会把键名本身打印出来)。
|
|
10751
11001
|
*/
|
|
10752
11002
|
const NS_STEP_CODE = "dsh-step-code";
|
|
10753
|
-
const zh$
|
|
11003
|
+
const zh$2 = {
|
|
10754
11004
|
...accountPoolZh,
|
|
10755
11005
|
title: "Step Code(Step Plan 订阅)",
|
|
10756
11006
|
pageDesc: "用 StepFun 的 Step Plan 订阅(国区 api.stepfun.com / 国际区 api.stepfun.ai)或开放平台 API Key 接入,管理账号池、套餐额度、缓存用量与模型勾选。区域是凭据的属性,两区凭据互不通用,因此号池里可以同时存在两区账号。",
|
|
@@ -10919,7 +11169,7 @@ window.__ModuleLoader__.load({
|
|
|
10919
11169
|
cacheEmpty: "本进程还没有任何请求回报过用量,跑一次对话后再看。",
|
|
10920
11170
|
cacheHitRatioUnavailable: "—"
|
|
10921
11171
|
};
|
|
10922
|
-
const en$
|
|
11172
|
+
const en$2 = {
|
|
10923
11173
|
...accountPoolEn,
|
|
10924
11174
|
title: "Step Code (Step Plan subscription)",
|
|
10925
11175
|
pageDesc: "Reach StepFun with a Step Plan subscription (China api.stepfun.com / international api.stepfun.ai) or an open-platform API key, and manage the account pool, plan quota, cache usage and model selection. A region is a property of the credential and the two never share, so a pool can carry accounts from both.",
|
|
@@ -11086,11 +11336,11 @@ window.__ModuleLoader__.load({
|
|
|
11086
11336
|
cacheEmpty: "No request on this machine has reported usage yet — run a conversation and look again.",
|
|
11087
11337
|
cacheHitRatioUnavailable: "—"
|
|
11088
11338
|
};
|
|
11089
|
-
const dictionaries$
|
|
11090
|
-
zh: zh$
|
|
11091
|
-
"zh-CN": zh$
|
|
11092
|
-
en: en$
|
|
11093
|
-
"en-US": en$
|
|
11339
|
+
const dictionaries$2 = {
|
|
11340
|
+
zh: zh$2,
|
|
11341
|
+
"zh-CN": zh$2,
|
|
11342
|
+
en: en$2,
|
|
11343
|
+
"en-US": en$2
|
|
11094
11344
|
};
|
|
11095
11345
|
/** 一个额度窗口的键到「窗口长度(分钟)」的映射。窗口名由共享组件按长度本地化。 */
|
|
11096
11346
|
const QUOTA_WINDOW_MINUTES = {
|
|
@@ -11208,7 +11458,7 @@ window.__ModuleLoader__.load({
|
|
|
11208
11458
|
/** 登录状态机的轮询节奏。真实超时与间隔由 host 拥有,卡片只是跟随。 */
|
|
11209
11459
|
const LOGIN_POLL_INTERVAL_MS = 2e3;
|
|
11210
11460
|
/** 本卡片的字典;身份稳定,依赖它的 effect 才不会每次渲染重建。 */
|
|
11211
|
-
const fallbackTranslate = (key) => zh$
|
|
11461
|
+
const fallbackTranslate$1 = (key) => zh$2[key];
|
|
11212
11462
|
/**
|
|
11213
11463
|
* 共享账号池卡片需要的标签集,按本卡片的字典逐键查出来。
|
|
11214
11464
|
*
|
|
@@ -11335,7 +11585,7 @@ window.__ModuleLoader__.load({
|
|
|
11335
11585
|
/** 写侧的线路常量;卡片用它渲染说明,而不是渲染一条永远为 0 的进度条。 */
|
|
11336
11586
|
const CACHE_WRITE_NOTE = STEP_CODE_CACHE_WRITE_NOTE;
|
|
11337
11587
|
function useStepCodeSection({ onModelChange } = {}) {
|
|
11338
|
-
const t = fallbackTranslate;
|
|
11588
|
+
const t = fallbackTranslate$1;
|
|
11339
11589
|
const [status, setStatus] = (0, react.useState)(null);
|
|
11340
11590
|
const [loading, setLoading] = (0, react.useState)(true);
|
|
11341
11591
|
const [busy, setBusy] = (0, react.useState)(null);
|
|
@@ -11892,7 +12142,7 @@ window.__ModuleLoader__.load({
|
|
|
11892
12142
|
const { status, loading, busy, error, connection, adoptResult, pasteValue, profile, pasteOpen, contextDrafts, savingModel, quotaAccount, quotaPassword, quotaToken } = state;
|
|
11893
12143
|
const { accounts, models, contextModels, overrideCount, login, quota, authenticated, poolLabels, cache, cacheHitRatio: hitRatio, quotaCredential } = derived;
|
|
11894
12144
|
const { setProfile, login: startLogin, cancelLogin, submitPaste, setPasteValue, togglePaste, adopt, clearAdoptResult, accountAction, setStrategy, toggleModel, setAllModels, saveContextWindow, resetContextWindow, resetAllContextWindows, updateContextDraft, setDefaultReasoningEffort, setCacheRequestParams, refreshQuota, refreshCatalog, testConnection, setQuotaAccount, setQuotaPassword, setQuotaToken, submitQuotaPassword, submitQuotaToken, clearQuotaCredential } = actions;
|
|
11895
|
-
const t = zh$
|
|
12145
|
+
const t = zh$2;
|
|
11896
12146
|
if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
11897
12147
|
className: "dsha-page",
|
|
11898
12148
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -12977,7 +13227,7 @@ window.__ModuleLoader__.load({
|
|
|
12977
13227
|
//#endregion
|
|
12978
13228
|
//#region src/client/locales.ts
|
|
12979
13229
|
const NS = "dsh-chatgpt-subscription";
|
|
12980
|
-
const dictionaries = {
|
|
13230
|
+
const dictionaries$1 = {
|
|
12981
13231
|
zh: {
|
|
12982
13232
|
...accountPoolZh,
|
|
12983
13233
|
title: "ChatGPT",
|
|
@@ -13331,7 +13581,7 @@ window.__ModuleLoader__.load({
|
|
|
13331
13581
|
}
|
|
13332
13582
|
//#endregion
|
|
13333
13583
|
//#region src/client/styles.ts
|
|
13334
|
-
const CSS$
|
|
13584
|
+
const CSS$3 = `
|
|
13335
13585
|
.dsh-codex-page{box-sizing:border-box;color:var(--dsw-alias-label-primary);display:flex;flex-direction:column;gap:20px;max-width:780px;min-width:0;padding:2px 0 30px}
|
|
13336
13586
|
.dsh-codex-page *{box-sizing:border-box}
|
|
13337
13587
|
.dsh-codex-title{font-size:15px;font-weight:650;line-height:1.4;margin:0 0 5px}
|
|
@@ -13454,7 +13704,7 @@ window.__ModuleLoader__.load({
|
|
|
13454
13704
|
* removed with, some other package.
|
|
13455
13705
|
*/
|
|
13456
13706
|
function installStyles() {
|
|
13457
|
-
return installPluginStyle("main", CSS$
|
|
13707
|
+
return installPluginStyle("main", CSS$3);
|
|
13458
13708
|
}
|
|
13459
13709
|
//#endregion
|
|
13460
13710
|
//#region src/client/hub/hub-styles.ts
|
|
@@ -13465,7 +13715,7 @@ window.__ModuleLoader__.load({
|
|
|
13465
13715
|
* common/plugin-style.ts). Colors ride DSH theme tokens, so dark mode needs
|
|
13466
13716
|
* no branch of its own.
|
|
13467
13717
|
*/
|
|
13468
|
-
const CSS$
|
|
13718
|
+
const CSS$2 = `
|
|
13469
13719
|
.dsh-hub-overview{display:flex;flex-direction:column;min-width:0}
|
|
13470
13720
|
.dsh-hub-hint{color:var(--dsw-alias-label-secondary,#5d5c52);font-size:13px;line-height:1.55;margin:0 0 14px}
|
|
13471
13721
|
.dsh-hub-cards{border-top:0.5px solid var(--dsw-alias-border-l2,#eceadf)}
|
|
@@ -13507,7 +13757,7 @@ window.__ModuleLoader__.load({
|
|
|
13507
13757
|
.dsh-hub-detail .dsha-page>header{margin-bottom:4px}
|
|
13508
13758
|
`;
|
|
13509
13759
|
function installHubStyles() {
|
|
13510
|
-
installPluginStyle("hub", CSS$
|
|
13760
|
+
installPluginStyle("hub", CSS$2);
|
|
13511
13761
|
}
|
|
13512
13762
|
//#endregion
|
|
13513
13763
|
//#region src/client/common/model-checklist-styles.ts
|
|
@@ -13516,7 +13766,7 @@ window.__ModuleLoader__.load({
|
|
|
13516
13766
|
* and hover follow the claude-style model picker so the settings list and the
|
|
13517
13767
|
* conversation picker read as one surface.
|
|
13518
13768
|
*/
|
|
13519
|
-
const CSS = `
|
|
13769
|
+
const CSS$1 = `
|
|
13520
13770
|
.dsh-mcl{display:flex;flex-direction:column;min-width:0;padding:4px 0 2px}
|
|
13521
13771
|
.dsh-mcl-head{align-items:center;display:flex;justify-content:space-between;min-height:28px;padding:2px 2px 6px}
|
|
13522
13772
|
.dsh-mcl-count{color:var(--dsw-alias-label-caption,#a6a094);font-size:11px;letter-spacing:.02em}
|
|
@@ -13540,7 +13790,7 @@ window.__ModuleLoader__.load({
|
|
|
13540
13790
|
.dsh-mcl-option:hover:not(:disabled) .dsh-mcl-check{border-color:var(--dsw-alias-button-info-fill,#397ee8)}
|
|
13541
13791
|
`;
|
|
13542
13792
|
function installModelChecklistStyles() {
|
|
13543
|
-
installPluginStyle("model-checklist", CSS);
|
|
13793
|
+
installPluginStyle("model-checklist", CSS$1);
|
|
13544
13794
|
}
|
|
13545
13795
|
//#endregion
|
|
13546
13796
|
//#region src/client/antigravity/styles.ts
|
|
@@ -14223,6 +14473,21 @@ window.__ModuleLoader__.load({
|
|
|
14223
14473
|
.dsha-account-add-actions{display:flex;flex-wrap:wrap;gap:8px}
|
|
14224
14474
|
.dsha-account-add-actions .dsha-btn{background:var(--dsw-alias-button-info-fill,#397ee8);border-color:transparent;color:var(--dsw-alias-button-info-label,#fff)}
|
|
14225
14475
|
.dsha-account-add-actions .dsha-btn:hover:not(:disabled){opacity:.9;background:var(--dsw-alias-button-info-fill,#397ee8)}
|
|
14476
|
+
/* Guardrails: one field per line, label left and control right — the same
|
|
14477
|
+
rhythm the context-window rows use, without a divider per row. Deliberately
|
|
14478
|
+
NOT .dsha-pref-row (a 58px full-width row with a border under it). */
|
|
14479
|
+
.dshwb-fields{display:grid;gap:10px;margin:10px 0 14px}
|
|
14480
|
+
.dshwb-field{align-items:center;display:flex;font-size:12px;gap:12px;justify-content:space-between}
|
|
14481
|
+
.dshwb-control{align-items:center;display:flex;gap:8px}
|
|
14482
|
+
.dshwb-control input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;padding:7px 9px;width:120px;text-align:right;box-sizing:border-box}
|
|
14483
|
+
.dshwb-control input:disabled{opacity:.5}
|
|
14484
|
+
.dshwb-control small{color:var(--dsw-alias-label-tertiary);font-size:11px;width:52px;text-align:left}
|
|
14485
|
+
/* One control row: a flexible input with its actions beside it, never wrapped
|
|
14486
|
+
into two ragged lines. */
|
|
14487
|
+
.dshwb-inputrow{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:10px 0 14px}
|
|
14488
|
+
.dshwb-inputrow>input{background:var(--dsw-alias-bg-layer-2);border:0.5px solid var(--dsw-alias-border-l2);border-radius:7px;color:var(--dsw-alias-label-primary);font:inherit;font-size:12px;padding:7px 9px;flex:1 1 260px;min-width:220px;max-width:420px;box-sizing:border-box}
|
|
14489
|
+
.dshwb-inputrow>input:disabled{opacity:.5}
|
|
14490
|
+
|
|
14226
14491
|
`, STYLE_ID$2);
|
|
14227
14492
|
}
|
|
14228
14493
|
//#endregion
|
|
@@ -14240,7 +14505,7 @@ window.__ModuleLoader__.load({
|
|
|
14240
14505
|
* 没有快照、没有窗口、或全部窗口都没有百分比时返回 null,即**不渲染胶囊**:
|
|
14241
14506
|
* 契约允许 quota 为 null,装一个占位数字比不显示更像坏了。
|
|
14242
14507
|
*/
|
|
14243
|
-
function selectBadgeFacts$1(status, lookup = (key) => zh$
|
|
14508
|
+
function selectBadgeFacts$1(status, lookup = (key) => zh$5[key]) {
|
|
14244
14509
|
const measured = (status?.quota?.windows ?? []).filter((window) => typeof window.usedPercent === "number" && Number.isFinite(window.usedPercent));
|
|
14245
14510
|
if (measured.length === 0) return null;
|
|
14246
14511
|
const tightest = measured.reduce((best, window) => {
|
|
@@ -14324,7 +14589,7 @@ window.__ModuleLoader__.load({
|
|
|
14324
14589
|
*
|
|
14325
14590
|
* 没有快照、没有窗口、或全部窗口都没有百分比时返回 null(见文件头)。
|
|
14326
14591
|
*/
|
|
14327
|
-
function selectBadgeFacts(status, lookup = (key) => zh$
|
|
14592
|
+
function selectBadgeFacts(status, lookup = (key) => zh$2[key]) {
|
|
14328
14593
|
const quota = status?.quota;
|
|
14329
14594
|
if (quota === null || quota === void 0) return null;
|
|
14330
14595
|
if (quota.kind !== "windowed") return null;
|
|
@@ -14780,6 +15045,1795 @@ window.__ModuleLoader__.load({
|
|
|
14780
15045
|
};
|
|
14781
15046
|
}
|
|
14782
15047
|
//#endregion
|
|
15048
|
+
//#region src/client/progress-updates/guide-artwork.tsx
|
|
15049
|
+
/**
|
|
15050
|
+
* 右侧栏 guide 页里「进展更新」那一格用的彩色标记。
|
|
15051
|
+
*
|
|
15052
|
+
* 为什么自带颜色:guide 的图标槽位收的是 `ComponentType<IconProps>`,平台自家的
|
|
15053
|
+
* 那几个(`plugin-artwork.tsx` 的 PluginArtworkTerminal / Loop / Subagent / Search)
|
|
15054
|
+
* 都**不骑 currentColor**,各自带品牌色与渐变——截图里那四格的颜色差异就是这么来的。
|
|
15055
|
+
* 所以这里照它们的样子画:36×36 viewBox、{size, className}、固定调色板。
|
|
15056
|
+
*
|
|
15057
|
+
* 图形语义:一条向上的折线(进展)加一个末端的箭头,与对话流里那一行的「状态点 +
|
|
15058
|
+
* 标题」互为同一件事的两种画法。渐变用 useId 生成前缀,避免同页多实例时
|
|
15059
|
+
* `url(#…)` 全部解析到第一个实例。
|
|
15060
|
+
*
|
|
15061
|
+
* @module @eddyskywalker/dsh-chatgpt-subscription/client/progress-updates/guide-artwork
|
|
15062
|
+
*/
|
|
15063
|
+
/**
|
|
15064
|
+
* 「进展更新」的彩色标记:一条攀升的折线,末端带箭头。
|
|
15065
|
+
* @param props - 尺寸与类名。
|
|
15066
|
+
* @returns 36×36 的彩色 SVG。
|
|
15067
|
+
*/
|
|
15068
|
+
function ProgressUpdatesArtwork({ size = 36, className }) {
|
|
15069
|
+
const uid = `dsh_progress_art_${(0, react.useId)().replaceAll(":", "")}`;
|
|
15070
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
15071
|
+
width: size,
|
|
15072
|
+
height: size,
|
|
15073
|
+
className,
|
|
15074
|
+
viewBox: "0 0 36 36",
|
|
15075
|
+
fill: "none",
|
|
15076
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
15077
|
+
"aria-hidden": "true",
|
|
15078
|
+
children: [
|
|
15079
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
15080
|
+
x: "3",
|
|
15081
|
+
y: "3",
|
|
15082
|
+
width: "30",
|
|
15083
|
+
height: "30",
|
|
15084
|
+
rx: "9",
|
|
15085
|
+
fill: `url(#${uid}bg)`,
|
|
15086
|
+
fillOpacity: "0.18"
|
|
15087
|
+
}),
|
|
15088
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
15089
|
+
d: "M9.5 24.2L15 18.6L19.2 22.2L26.5 13.4",
|
|
15090
|
+
stroke: `url(#${uid}line)`,
|
|
15091
|
+
strokeWidth: "2.8",
|
|
15092
|
+
strokeLinecap: "round",
|
|
15093
|
+
strokeLinejoin: "round"
|
|
15094
|
+
}),
|
|
15095
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
15096
|
+
d: "M21.6 12.8H27.2V18.4",
|
|
15097
|
+
stroke: "#7C6AEF",
|
|
15098
|
+
strokeWidth: "2.8",
|
|
15099
|
+
strokeLinecap: "round",
|
|
15100
|
+
strokeLinejoin: "round"
|
|
15101
|
+
}),
|
|
15102
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("defs", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("linearGradient", {
|
|
15103
|
+
id: `${uid}bg`,
|
|
15104
|
+
x1: "3",
|
|
15105
|
+
y1: "3",
|
|
15106
|
+
x2: "33",
|
|
15107
|
+
y2: "33",
|
|
15108
|
+
gradientUnits: "userSpaceOnUse",
|
|
15109
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", { stopColor: "#658EFF" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
|
|
15110
|
+
offset: "1",
|
|
15111
|
+
stopColor: "#A797FC"
|
|
15112
|
+
})]
|
|
15113
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("linearGradient", {
|
|
15114
|
+
id: `${uid}line`,
|
|
15115
|
+
x1: "9.5",
|
|
15116
|
+
y1: "24.2",
|
|
15117
|
+
x2: "26.5",
|
|
15118
|
+
y2: "13.4",
|
|
15119
|
+
gradientUnits: "userSpaceOnUse",
|
|
15120
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", { stopColor: "#4561EE" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("stop", {
|
|
15121
|
+
offset: "1",
|
|
15122
|
+
stopColor: "#7C6AEF"
|
|
15123
|
+
})]
|
|
15124
|
+
})] })
|
|
15125
|
+
]
|
|
15126
|
+
});
|
|
15127
|
+
}
|
|
15128
|
+
//#endregion
|
|
15129
|
+
//#region src/client/progress-updates/markdown-lite.tsx
|
|
15130
|
+
/** 行内标记:反引号代码与双星号粗体。两者可混排。 */
|
|
15131
|
+
const INLINE = /(`[^`]+`)|(\*\*[^*]+\*\*)/g;
|
|
15132
|
+
/**
|
|
15133
|
+
* 渲染一段行内文本。
|
|
15134
|
+
* @param text - 原始行内文本。
|
|
15135
|
+
* @param keyPrefix - 生成 React key 的前缀。
|
|
15136
|
+
* @returns 由文本、code、strong 组成的节点数组。
|
|
15137
|
+
*/
|
|
15138
|
+
function renderInline(text, keyPrefix) {
|
|
15139
|
+
const nodes = [];
|
|
15140
|
+
let last = 0;
|
|
15141
|
+
let index = 0;
|
|
15142
|
+
for (const match of text.matchAll(INLINE)) {
|
|
15143
|
+
const at = match.index ?? 0;
|
|
15144
|
+
if (at > last) nodes.push(text.slice(last, at));
|
|
15145
|
+
const whole = match[0];
|
|
15146
|
+
const key = keyPrefix + "-" + String(index);
|
|
15147
|
+
if (whole.startsWith("`")) nodes.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
15148
|
+
"data-dsh-part": "md-code",
|
|
15149
|
+
children: whole.slice(1, -1)
|
|
15150
|
+
}, key));
|
|
15151
|
+
else nodes.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
|
|
15152
|
+
"data-dsh-part": "md-strong",
|
|
15153
|
+
children: whole.slice(2, -2)
|
|
15154
|
+
}, key));
|
|
15155
|
+
last = at + whole.length;
|
|
15156
|
+
index += 1;
|
|
15157
|
+
}
|
|
15158
|
+
if (last < text.length) nodes.push(text.slice(last));
|
|
15159
|
+
return nodes;
|
|
15160
|
+
}
|
|
15161
|
+
/**
|
|
15162
|
+
* 把正文切成块。
|
|
15163
|
+
* @param source - Markdown 原文。
|
|
15164
|
+
* @returns 按出现顺序排列的块。
|
|
15165
|
+
*/
|
|
15166
|
+
function parseBlocks(source) {
|
|
15167
|
+
const blocks = [];
|
|
15168
|
+
let paragraph = [];
|
|
15169
|
+
const flushParagraph = () => {
|
|
15170
|
+
if (paragraph.length > 0) {
|
|
15171
|
+
blocks.push({
|
|
15172
|
+
kind: "p",
|
|
15173
|
+
lines: paragraph
|
|
15174
|
+
});
|
|
15175
|
+
paragraph = [];
|
|
15176
|
+
}
|
|
15177
|
+
};
|
|
15178
|
+
const lastBlock = () => blocks[blocks.length - 1];
|
|
15179
|
+
for (const rawLine of source.replaceAll("\r\n", "\n").split("\n")) {
|
|
15180
|
+
const line = rawLine.trimEnd();
|
|
15181
|
+
const trimmed = line.trim();
|
|
15182
|
+
if (trimmed === "") {
|
|
15183
|
+
flushParagraph();
|
|
15184
|
+
continue;
|
|
15185
|
+
}
|
|
15186
|
+
const heading = /^(#{1,3})\s+(.*)$/.exec(trimmed);
|
|
15187
|
+
if (heading !== null) {
|
|
15188
|
+
flushParagraph();
|
|
15189
|
+
blocks.push({
|
|
15190
|
+
kind: "h",
|
|
15191
|
+
level: heading[1].length,
|
|
15192
|
+
text: heading[2]
|
|
15193
|
+
});
|
|
15194
|
+
continue;
|
|
15195
|
+
}
|
|
15196
|
+
const ordered = /^\d+[.)]\s+(.*)$/.exec(trimmed);
|
|
15197
|
+
if (ordered !== null) {
|
|
15198
|
+
flushParagraph();
|
|
15199
|
+
const previous = lastBlock();
|
|
15200
|
+
if (previous?.kind === "ol") previous.items.push(ordered[1]);
|
|
15201
|
+
else blocks.push({
|
|
15202
|
+
kind: "ol",
|
|
15203
|
+
items: [ordered[1]]
|
|
15204
|
+
});
|
|
15205
|
+
continue;
|
|
15206
|
+
}
|
|
15207
|
+
const unordered = /^[-*\u2022]\s+(.*)$/.exec(trimmed);
|
|
15208
|
+
if (unordered !== null) {
|
|
15209
|
+
flushParagraph();
|
|
15210
|
+
const previous = lastBlock();
|
|
15211
|
+
if (previous?.kind === "ul") previous.items.push(unordered[1]);
|
|
15212
|
+
else blocks.push({
|
|
15213
|
+
kind: "ul",
|
|
15214
|
+
items: [unordered[1]]
|
|
15215
|
+
});
|
|
15216
|
+
continue;
|
|
15217
|
+
}
|
|
15218
|
+
paragraph.push(line);
|
|
15219
|
+
}
|
|
15220
|
+
flushParagraph();
|
|
15221
|
+
return blocks;
|
|
15222
|
+
}
|
|
15223
|
+
/**
|
|
15224
|
+
* 把一条进展的正文渲染成 React 元素。
|
|
15225
|
+
* @param props - 正文原文。
|
|
15226
|
+
* @returns 块级元素;空正文渲染空片段。
|
|
15227
|
+
*/
|
|
15228
|
+
function MarkdownLite({ text }) {
|
|
15229
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: parseBlocks(text).map((block, blockIndex) => {
|
|
15230
|
+
const key = "b" + String(blockIndex);
|
|
15231
|
+
if (block.kind === "h") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
15232
|
+
"data-dsh-part": "md-h",
|
|
15233
|
+
style: {
|
|
15234
|
+
margin: "8px 0 3px",
|
|
15235
|
+
fontSize: block.level === 1 ? "15px" : block.level === 2 ? "14px" : "13px",
|
|
15236
|
+
fontWeight: 600
|
|
15237
|
+
},
|
|
15238
|
+
children: renderInline(block.text, key)
|
|
15239
|
+
}, key);
|
|
15240
|
+
if (block.kind === "ul" || block.kind === "ol") {
|
|
15241
|
+
const items = block.items.map((item, itemIndex) => {
|
|
15242
|
+
const itemKey = key + "-" + String(itemIndex);
|
|
15243
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
15244
|
+
style: { margin: "2px 0" },
|
|
15245
|
+
children: renderInline(item, itemKey)
|
|
15246
|
+
}, itemKey);
|
|
15247
|
+
});
|
|
15248
|
+
return block.kind === "ul" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
15249
|
+
"data-dsh-part": "md-ul",
|
|
15250
|
+
style: {
|
|
15251
|
+
margin: "5px 0",
|
|
15252
|
+
paddingLeft: "18px"
|
|
15253
|
+
},
|
|
15254
|
+
children: items
|
|
15255
|
+
}, key) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
|
|
15256
|
+
"data-dsh-part": "md-ol",
|
|
15257
|
+
style: {
|
|
15258
|
+
margin: "5px 0",
|
|
15259
|
+
paddingLeft: "18px"
|
|
15260
|
+
},
|
|
15261
|
+
children: items
|
|
15262
|
+
}, key);
|
|
15263
|
+
}
|
|
15264
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
15265
|
+
"data-dsh-part": "md-p",
|
|
15266
|
+
style: {
|
|
15267
|
+
margin: "0 0 7px",
|
|
15268
|
+
whiteSpace: "pre-wrap"
|
|
15269
|
+
},
|
|
15270
|
+
children: renderInline(block.lines.join("\n"), key)
|
|
15271
|
+
}, key);
|
|
15272
|
+
}) });
|
|
15273
|
+
}
|
|
15274
|
+
//#endregion
|
|
15275
|
+
//#region src/client/progress-updates/locales.ts
|
|
15276
|
+
/**
|
|
15277
|
+
* 「进展更新」在 locale 服务里的命名空间。
|
|
15278
|
+
*
|
|
15279
|
+
* 跟着本插件其余线路的命名习惯走(`dsh-kimi-code`、`dsh-step-code`、`dsh-ollama`
|
|
15280
|
+
* ……都是「dsh-<线路>」):新线路并入本插件的字典体系,而不是另开一个与包名无关
|
|
15281
|
+
* 的命名空间。
|
|
15282
|
+
*/
|
|
15283
|
+
const NS_PROGRESS_UPDATES = "dsh-progress-updates";
|
|
15284
|
+
/**
|
|
15285
|
+
* 中文文案。
|
|
15286
|
+
*
|
|
15287
|
+
* 键名沿用本插件其余字典的扁平驼峰(`hubTitle`、`imageToolRunning` 那一套),
|
|
15288
|
+
* 不另起一套点号命名——同一份 index.tsx 里两种命名风格会让「这条键属于哪张表」
|
|
15289
|
+
* 变成要靠记忆的事。
|
|
15290
|
+
*/
|
|
15291
|
+
const zh = {
|
|
15292
|
+
/** 设置页导航里的那一格,也是侧栏页签的标题。 */
|
|
15293
|
+
navTitle: "进展更新",
|
|
15294
|
+
progressTitle: "进展更新",
|
|
15295
|
+
/** 右侧栏 guide 卡片标题下的一行说明。 */
|
|
15296
|
+
progressGuideNote: "agent 汇报的每一步进展,都收在这里",
|
|
15297
|
+
markdownCopy: "复制",
|
|
15298
|
+
markdownCopied: "已复制",
|
|
15299
|
+
markdownFootnotes: "脚注",
|
|
15300
|
+
progressLatest: "最新",
|
|
15301
|
+
progressCount: "共 {n} 条",
|
|
15302
|
+
progressExpand: "展开这条进展",
|
|
15303
|
+
progressCollapse: "收起这条进展",
|
|
15304
|
+
progressEmptyTitle: "还没有进展更新",
|
|
15305
|
+
progressEmptyHint: "agent 开始多步任务后,会在这里汇报它进行到哪一步。",
|
|
15306
|
+
rowPending: "进展更新",
|
|
15307
|
+
rowRunning: "进行中",
|
|
15308
|
+
rowDone: "已汇报",
|
|
15309
|
+
rowError: "未发出",
|
|
15310
|
+
rowExpand: "展开这条进展的详情",
|
|
15311
|
+
rowCollapse: "收起这条进展的详情",
|
|
15312
|
+
settingsHint: "进展更新会让 agent 在多步任务中主动汇报进度。打开面板可以看它到目前为止的全部汇报。",
|
|
15313
|
+
settingsOpen: "打开进展面板",
|
|
15314
|
+
settingsEnabledName: "进展更新",
|
|
15315
|
+
settingsEnabledNote: "让 agent 在对话中发布进展更新",
|
|
15316
|
+
settingsThresholdName: "催促阈值",
|
|
15317
|
+
settingsThresholdNote: "连续这么多轮没有进展时,提醒 agent 汇报一次",
|
|
15318
|
+
settingsPanelName: "进展面板",
|
|
15319
|
+
settingsPanelNote: "在右侧栏查看本次会话的全部进展",
|
|
15320
|
+
settingsUnavailable: "这项设置当前不可修改(宿主没有开放配置表单)",
|
|
15321
|
+
settingsSaveFailed: "保存失败,请稍后再试"
|
|
15322
|
+
};
|
|
15323
|
+
/** 英文对照,键集与 zh 完全一致。 */
|
|
15324
|
+
const en = {
|
|
15325
|
+
navTitle: "Progress updates",
|
|
15326
|
+
progressTitle: "Progress updates",
|
|
15327
|
+
progressGuideNote: "Every step the agent reports, collected here",
|
|
15328
|
+
markdownCopy: "Copy",
|
|
15329
|
+
markdownCopied: "Copied",
|
|
15330
|
+
markdownFootnotes: "Footnotes",
|
|
15331
|
+
progressLatest: "Latest",
|
|
15332
|
+
progressCount: "{n} total",
|
|
15333
|
+
progressExpand: "Expand this update",
|
|
15334
|
+
progressCollapse: "Collapse this update",
|
|
15335
|
+
progressEmptyTitle: "No progress updates yet",
|
|
15336
|
+
progressEmptyHint: "Once the agent starts multi-step work, it reports how far it has got right here.",
|
|
15337
|
+
rowPending: "Progress update",
|
|
15338
|
+
rowRunning: "Reporting",
|
|
15339
|
+
rowDone: "Reported",
|
|
15340
|
+
rowError: "Not reported",
|
|
15341
|
+
rowExpand: "Expand this progress update",
|
|
15342
|
+
rowCollapse: "Collapse this progress update",
|
|
15343
|
+
settingsHint: "Progress updates let the agent report how far multi-step work has got. Open the panel to read everything it has reported so far.",
|
|
15344
|
+
settingsOpen: "Open progress panel",
|
|
15345
|
+
settingsEnabledName: "Progress updates",
|
|
15346
|
+
settingsEnabledNote: "Let the agent post progress updates in the conversation",
|
|
15347
|
+
settingsThresholdName: "Nudge threshold",
|
|
15348
|
+
settingsThresholdNote: "Remind the agent after this many rounds without an update",
|
|
15349
|
+
settingsPanelName: "Progress panel",
|
|
15350
|
+
settingsPanelNote: "See every update from this session in the right sidebar",
|
|
15351
|
+
settingsUnavailable: "This setting cannot be changed right now (the host serves no config form)",
|
|
15352
|
+
settingsSaveFailed: "Could not save. Try again in a moment."
|
|
15353
|
+
};
|
|
15354
|
+
/** 与其余线路同形的字典包(四个内置语言 id 都指向那两份)。 */
|
|
15355
|
+
const dictionaries = {
|
|
15356
|
+
zh,
|
|
15357
|
+
"zh-CN": zh,
|
|
15358
|
+
en,
|
|
15359
|
+
"en-US": en
|
|
15360
|
+
};
|
|
15361
|
+
//#endregion
|
|
15362
|
+
//#region src/client/progress-updates/updates-panel.tsx
|
|
15363
|
+
/**
|
|
15364
|
+
* 进展更新面板:把 `progress_update` 工具产生的进展按时间顺序列出来。
|
|
15365
|
+
*
|
|
15366
|
+
* 交互照 Kimi Code:**最新一条默认展开,更早的自动收起,点标题可切换**。
|
|
15367
|
+
* 每条只显示一行标题;展开时才渲染详情正文(保留换行,不做完整 Markdown
|
|
15368
|
+
* 渲染 —— 契约里限定的就是「一段正文 + 少量 bullet」)。
|
|
15369
|
+
*
|
|
15370
|
+
* 数据源:纯展示组件只认一个 `updates` 数组 prop(见 {@link UpdatesPanelProps})。
|
|
15371
|
+
* 真实的取数通道是宿主路由 `GET /api/dsh-progress-updates/updates?session=<id>`,
|
|
15372
|
+
* 浏览器半边的轮询客户端在 `./host-source.ts`(`createHostProgressSource`),它
|
|
15373
|
+
* 把结果包成 {@link ProgressSnapshotSource} 交给入口。{@link EMPTY_SOURCE} 只剩
|
|
15374
|
+
* 两个用途:没有会话身份时的兜底,以及测试里的空快照参照。
|
|
15375
|
+
*
|
|
15376
|
+
* 文案全部走 locale(本插件 `dsh-progress-updates` 命名空间),文件里不出现
|
|
15377
|
+
* 硬编码中文。
|
|
15378
|
+
*
|
|
15379
|
+
* @module @eddyskywalker/dsh-chatgpt-subscription/client/progress-updates/updates-panel
|
|
15380
|
+
*/
|
|
15381
|
+
/**
|
|
15382
|
+
* 兜底翻译:只在本组件被渲染在 slot 框架之外时用到(jsdom 测试、故事预览)。
|
|
15383
|
+
* 走同一份 zh 字典,因此不存在第二套文案。框架内渲染时用注入的 `t` 座位,
|
|
15384
|
+
* 这样第三方语言包也能生效。
|
|
15385
|
+
* @param key - `dsh-progress-updates` 命名空间的键。
|
|
15386
|
+
* @param params - `{name}` 模板参数。
|
|
15387
|
+
* @returns 该键的文案;缺键时回退为键本身。
|
|
15388
|
+
*/
|
|
15389
|
+
function fallbackTranslate(key, params) {
|
|
15390
|
+
return interpolate(zh[key] ?? key, params);
|
|
15391
|
+
}
|
|
15392
|
+
/**
|
|
15393
|
+
* 把 `{name}` 占位符换成实参。
|
|
15394
|
+
*
|
|
15395
|
+
* 单独成一个函数,是因为**两条取值路径都要用它**:没有 locale 服务的兜底字典,
|
|
15396
|
+
* 和有服务时的当前语言字典。早先只有兜底那条做了替换,于是真实部署里
|
|
15397
|
+
* 「共 {n} 条」原样上屏。
|
|
15398
|
+
* @param template - 含占位符的模板。
|
|
15399
|
+
* @param params - 各占位符的实参。
|
|
15400
|
+
* @returns 替换后的文本;没有 params 时原样返回。
|
|
15401
|
+
*/
|
|
15402
|
+
function interpolate(template, params) {
|
|
15403
|
+
if (params === void 0) return template;
|
|
15404
|
+
let text = template;
|
|
15405
|
+
for (const [name, value] of Object.entries(params)) text = text.replaceAll(`{${name}}`, String(value));
|
|
15406
|
+
return text;
|
|
15407
|
+
}
|
|
15408
|
+
/** 一份空进展快照。 */
|
|
15409
|
+
const EMPTY_UPDATES = Object.freeze([]);
|
|
15410
|
+
Object.freeze({
|
|
15411
|
+
getSnapshot: () => EMPTY_UPDATES,
|
|
15412
|
+
subscribe: () => () => {}
|
|
15413
|
+
});
|
|
15414
|
+
/** 面板容器样式。 */
|
|
15415
|
+
const panelStyle = {
|
|
15416
|
+
display: "flex",
|
|
15417
|
+
flexDirection: "column",
|
|
15418
|
+
gap: "8px",
|
|
15419
|
+
padding: "10px 12px 16px",
|
|
15420
|
+
minHeight: "0",
|
|
15421
|
+
overflowY: "auto",
|
|
15422
|
+
fontSize: "12px",
|
|
15423
|
+
color: "var(--dsw-alias-label-primary, #1f2329)"
|
|
15424
|
+
};
|
|
15425
|
+
/** 一条进展的外框样式。 */
|
|
15426
|
+
const rowStyle$1 = {
|
|
15427
|
+
display: "flex",
|
|
15428
|
+
flexDirection: "column",
|
|
15429
|
+
gap: "6px",
|
|
15430
|
+
border: "1px solid var(--dsw-alias-border-l2, rgb(0 0 0 / 10%))",
|
|
15431
|
+
borderRadius: "8px",
|
|
15432
|
+
background: "var(--dsw-alias-bg-layer-1, rgb(255 255 255 / 60%))",
|
|
15433
|
+
padding: "8px 10px"
|
|
15434
|
+
};
|
|
15435
|
+
/** 行标题按钮样式(整行可点,去掉原生按钮外观)。 */
|
|
15436
|
+
const headerStyle$1 = {
|
|
15437
|
+
display: "flex",
|
|
15438
|
+
alignItems: "flex-start",
|
|
15439
|
+
gap: "8px",
|
|
15440
|
+
width: "100%",
|
|
15441
|
+
padding: "0",
|
|
15442
|
+
border: "0",
|
|
15443
|
+
background: "transparent",
|
|
15444
|
+
color: "inherit",
|
|
15445
|
+
font: "inherit",
|
|
15446
|
+
textAlign: "left",
|
|
15447
|
+
cursor: "pointer"
|
|
15448
|
+
};
|
|
15449
|
+
/** 标题文本样式。 */
|
|
15450
|
+
const titleStyle$1 = {
|
|
15451
|
+
flex: "1 1 auto",
|
|
15452
|
+
minWidth: "0",
|
|
15453
|
+
fontSize: "12px",
|
|
15454
|
+
fontWeight: 500,
|
|
15455
|
+
lineHeight: "1.5",
|
|
15456
|
+
overflowWrap: "anywhere"
|
|
15457
|
+
};
|
|
15458
|
+
/** 时间戳/徽标的小字样式。 */
|
|
15459
|
+
const metaStyle = {
|
|
15460
|
+
flex: "0 0 auto",
|
|
15461
|
+
fontSize: "11px",
|
|
15462
|
+
lineHeight: "1.5",
|
|
15463
|
+
color: "var(--dsw-alias-label-tertiary, #8a9099)"
|
|
15464
|
+
};
|
|
15465
|
+
/**
|
|
15466
|
+
* 展开行正文的容器样式。
|
|
15467
|
+
*
|
|
15468
|
+
* 正文本身交给平台的 `MarkdownText` 渲染(GFM、列表、行内代码、表格、KaTeX 全都在
|
|
15469
|
+
* 那一套里),这里只负责外边距、字号与颜色:`pre-wrap` 与手写解析都撤掉了,
|
|
15470
|
+
* 因为 Markdown 的块级结构必须由渲染器产出的元素决定,纯文本的换行保留只会把
|
|
15471
|
+
* 列表符号与星号原样显示出来。
|
|
15472
|
+
*/
|
|
15473
|
+
const messageStyle$1 = {
|
|
15474
|
+
margin: "2px 0 0",
|
|
15475
|
+
fontSize: "13px",
|
|
15476
|
+
lineHeight: "1.6",
|
|
15477
|
+
color: "var(--dsw-alias-label-secondary, #5b6270)"
|
|
15478
|
+
};
|
|
15479
|
+
/** 空态容器样式。 */
|
|
15480
|
+
const emptyStyle = {
|
|
15481
|
+
display: "flex",
|
|
15482
|
+
flexDirection: "column",
|
|
15483
|
+
gap: "4px",
|
|
15484
|
+
padding: "16px 4px",
|
|
15485
|
+
color: "var(--dsw-alias-label-tertiary, #8a9099)"
|
|
15486
|
+
};
|
|
15487
|
+
/**
|
|
15488
|
+
* 一天的短格式时间(HH:MM)。只输出数字与分隔符,与语言无关。
|
|
15489
|
+
* @param at - Unix 毫秒时间戳。
|
|
15490
|
+
* @returns 本地时区的 HH:MM;非法时间戳返回空串。
|
|
15491
|
+
*/
|
|
15492
|
+
function clockOf(at) {
|
|
15493
|
+
if (!Number.isFinite(at)) return "";
|
|
15494
|
+
try {
|
|
15495
|
+
return new Date(at).toLocaleTimeString([], {
|
|
15496
|
+
hour: "2-digit",
|
|
15497
|
+
minute: "2-digit"
|
|
15498
|
+
});
|
|
15499
|
+
} catch {
|
|
15500
|
+
return "";
|
|
15501
|
+
}
|
|
15502
|
+
}
|
|
15503
|
+
/**
|
|
15504
|
+
* 标题行文本:没有 title 时用正文首行,保证每行都有一个可读标题。
|
|
15505
|
+
* @param update - 一条进展。
|
|
15506
|
+
* @returns 标题文本。
|
|
15507
|
+
*/
|
|
15508
|
+
function titleOf(update) {
|
|
15509
|
+
if (update.title !== void 0 && update.title.trim() !== "") return update.title;
|
|
15510
|
+
return update.message.split("\n").find((line) => line.trim() !== "") ?? "";
|
|
15511
|
+
}
|
|
15512
|
+
/**
|
|
15513
|
+
* 展开指示箭头,跟随展开状态旋转。
|
|
15514
|
+
* @param props - 是否展开。
|
|
15515
|
+
* @returns 装饰性箭头图标。
|
|
15516
|
+
*/
|
|
15517
|
+
function Chevron$1({ expanded }) {
|
|
15518
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
15519
|
+
viewBox: "0 0 16 16",
|
|
15520
|
+
width: "12",
|
|
15521
|
+
height: "12",
|
|
15522
|
+
fill: "none",
|
|
15523
|
+
stroke: "currentColor",
|
|
15524
|
+
strokeWidth: "1.4",
|
|
15525
|
+
strokeLinecap: "round",
|
|
15526
|
+
strokeLinejoin: "round",
|
|
15527
|
+
"aria-hidden": "true",
|
|
15528
|
+
style: {
|
|
15529
|
+
flex: "0 0 auto",
|
|
15530
|
+
marginTop: "3px",
|
|
15531
|
+
transform: expanded ? "rotate(90deg)" : "none",
|
|
15532
|
+
transition: "transform 120ms ease"
|
|
15533
|
+
},
|
|
15534
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "m6 4 4 4-4 4" })
|
|
15535
|
+
});
|
|
15536
|
+
}
|
|
15537
|
+
/**
|
|
15538
|
+
* 侧栏进展面板(纯展示)。
|
|
15539
|
+
* @param props - 进展数组与可选的注入文案座位。
|
|
15540
|
+
* @returns 进展列表;没有进展时渲染占位空态。
|
|
15541
|
+
*/
|
|
15542
|
+
function ProgressUpdatesPanel({ updates, t }) {
|
|
15543
|
+
/**
|
|
15544
|
+
* 翻译并**补齐占位符**。
|
|
15545
|
+
*
|
|
15546
|
+
* locale 服务交来的 `t` 只接受一个键、不做插值,所以参数必须在这一层补上:
|
|
15547
|
+
* 直接把 `t(key)` 当作结果会留下原样的 `{n}`(线上真出现过「共 {n} 条」)。
|
|
15548
|
+
* 先按当前语言的字典取值(没有 `t` 时才用兜底字典),再统一做一次替换。
|
|
15549
|
+
*/
|
|
15550
|
+
const translate = (key, params) => {
|
|
15551
|
+
return interpolate(t === void 0 ? zh[key] ?? key : t(key), params);
|
|
15552
|
+
};
|
|
15553
|
+
const latestId = updates.length > 0 ? updates[updates.length - 1].id : void 0;
|
|
15554
|
+
/**
|
|
15555
|
+
* 展开的进展集合 —— **不是**单个 id。
|
|
15556
|
+
*
|
|
15557
|
+
* 早先这里是单值 `expandedId`,于是展开下面那条会把上面那条关掉:用户想对照着
|
|
15558
|
+
* 看两条进展时永远只能看到一条。展开是逐条的独立选择,所以用集合。
|
|
15559
|
+
*
|
|
15560
|
+
* 只写「显式展开过/收起过」的状态,不在渲染期推导:一条新进展到达时,把最新的
|
|
15561
|
+
* 那条追加进集合(默认展开最新),但**不动**用户已经展开的其它条目。
|
|
15562
|
+
*/
|
|
15563
|
+
const [expandedIds, setExpandedIds] = (0, react.useState)(() => new Set(latestId === void 0 ? [] : [latestId]));
|
|
15564
|
+
(0, react.useEffect)(() => {
|
|
15565
|
+
if (latestId === void 0) return;
|
|
15566
|
+
setExpandedIds((current) => {
|
|
15567
|
+
if (current.has(latestId)) return current;
|
|
15568
|
+
const next = new Set(current);
|
|
15569
|
+
next.add(latestId);
|
|
15570
|
+
return next;
|
|
15571
|
+
});
|
|
15572
|
+
}, [latestId]);
|
|
15573
|
+
const idPrefix = (0, react.useId)();
|
|
15574
|
+
if (updates.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15575
|
+
"data-dsh-plugin": "progress-updates",
|
|
15576
|
+
"data-dsh-part": "updates",
|
|
15577
|
+
style: panelStyle,
|
|
15578
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15579
|
+
"data-dsh-part": "updates-empty",
|
|
15580
|
+
style: emptyStyle,
|
|
15581
|
+
role: "status",
|
|
15582
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
|
|
15583
|
+
style: {
|
|
15584
|
+
fontSize: "12px",
|
|
15585
|
+
fontWeight: 500
|
|
15586
|
+
},
|
|
15587
|
+
children: translate("progressEmptyTitle")
|
|
15588
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: translate("progressEmptyHint") })]
|
|
15589
|
+
})
|
|
15590
|
+
});
|
|
15591
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15592
|
+
"data-dsh-plugin": "progress-updates",
|
|
15593
|
+
"data-dsh-part": "updates",
|
|
15594
|
+
style: panelStyle,
|
|
15595
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15596
|
+
"data-dsh-part": "updates-count",
|
|
15597
|
+
style: metaStyle,
|
|
15598
|
+
children: translate("progressCount", { n: updates.length })
|
|
15599
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ol", {
|
|
15600
|
+
"data-dsh-part": "updates-list",
|
|
15601
|
+
style: {
|
|
15602
|
+
listStyle: "none",
|
|
15603
|
+
margin: "0",
|
|
15604
|
+
padding: "0",
|
|
15605
|
+
display: "flex",
|
|
15606
|
+
flexDirection: "column",
|
|
15607
|
+
gap: "8px"
|
|
15608
|
+
},
|
|
15609
|
+
children: updates.map((update, index) => {
|
|
15610
|
+
const expanded = expandedIds.has(update.id);
|
|
15611
|
+
const latest = update.id === latestId;
|
|
15612
|
+
const title = titleOf(update);
|
|
15613
|
+
const bodyId = `${idPrefix}-${index}-body`;
|
|
15614
|
+
const clock = clockOf(update.at);
|
|
15615
|
+
const toggleLabel = translate(expanded ? "progressCollapse" : "progressExpand");
|
|
15616
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
15617
|
+
"data-dsh-part": "update",
|
|
15618
|
+
"data-update-id": update.id,
|
|
15619
|
+
style: rowStyle$1,
|
|
15620
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
15621
|
+
type: "button",
|
|
15622
|
+
"data-dsh-part": "update-header",
|
|
15623
|
+
"aria-expanded": expanded,
|
|
15624
|
+
"aria-controls": bodyId,
|
|
15625
|
+
"aria-label": toggleLabel,
|
|
15626
|
+
title: toggleLabel,
|
|
15627
|
+
style: headerStyle$1,
|
|
15628
|
+
onClick: () => {
|
|
15629
|
+
setExpandedIds((current) => {
|
|
15630
|
+
const next = new Set(current);
|
|
15631
|
+
if (next.has(update.id)) next.delete(update.id);
|
|
15632
|
+
else next.add(update.id);
|
|
15633
|
+
return next;
|
|
15634
|
+
});
|
|
15635
|
+
},
|
|
15636
|
+
children: [
|
|
15637
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron$1, { expanded }),
|
|
15638
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15639
|
+
"data-dsh-part": "update-title",
|
|
15640
|
+
style: titleStyle$1,
|
|
15641
|
+
children: title
|
|
15642
|
+
}),
|
|
15643
|
+
latest && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15644
|
+
"data-dsh-part": "update-badge",
|
|
15645
|
+
style: {
|
|
15646
|
+
...metaStyle,
|
|
15647
|
+
color: "var(--dsw-alias-brand-primary, #4d6bfe)"
|
|
15648
|
+
},
|
|
15649
|
+
children: translate("progressLatest")
|
|
15650
|
+
}),
|
|
15651
|
+
clock !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
|
|
15652
|
+
dateTime: new Date(update.at).toISOString(),
|
|
15653
|
+
style: metaStyle,
|
|
15654
|
+
children: clock
|
|
15655
|
+
})
|
|
15656
|
+
]
|
|
15657
|
+
}), expanded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
15658
|
+
id: bodyId,
|
|
15659
|
+
"data-dsh-part": "update-message",
|
|
15660
|
+
style: messageStyle$1,
|
|
15661
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MarkdownLite, { text: update.message })
|
|
15662
|
+
})]
|
|
15663
|
+
}, update.id);
|
|
15664
|
+
})
|
|
15665
|
+
})]
|
|
15666
|
+
});
|
|
15667
|
+
}
|
|
15668
|
+
/**
|
|
15669
|
+
* 面板正文的槽位组件:把注入的数据源接到纯展示组件上。
|
|
15670
|
+
*
|
|
15671
|
+
* 用 `useSyncExternalStore` 订阅而不是直接读一次快照:轮询源每次拿到新进展都会
|
|
15672
|
+
* 通知,这里自动重渲染;展示组件本身不关心数据是怎么来的。
|
|
15673
|
+
* @param props - 框架注入的数据源与文案座位。
|
|
15674
|
+
* @returns 进展面板。
|
|
15675
|
+
*/
|
|
15676
|
+
function ProgressUpdatesTab(props) {
|
|
15677
|
+
const { source, t } = props;
|
|
15678
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProgressUpdatesPanel, {
|
|
15679
|
+
updates: (0, react.useSyncExternalStore)(source.subscribe, source.getSnapshot, source.getSnapshot),
|
|
15680
|
+
t
|
|
15681
|
+
});
|
|
15682
|
+
}
|
|
15683
|
+
/**
|
|
15684
|
+
* 页签标题的槽位组件:标签条上显示的那几个字。
|
|
15685
|
+
* @param props - 框架注入的文案座位。
|
|
15686
|
+
* @returns 面板标题文本。
|
|
15687
|
+
*/
|
|
15688
|
+
function ProgressUpdatesTabTitle({ t }) {
|
|
15689
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: t === void 0 ? fallbackTranslate("progressTitle") : t("progressTitle") });
|
|
15690
|
+
}
|
|
15691
|
+
//#endregion
|
|
15692
|
+
//#region src/client/progress-updates/progress-row.tsx
|
|
15693
|
+
/**
|
|
15694
|
+
* 对话流里的「进展行」——本插件的主形态。
|
|
15695
|
+
*
|
|
15696
|
+
* agent 每发一条进展,对话里就多一行:标题行(状态点 + 标题 + 状态词)常显,
|
|
15697
|
+
* 正文默认收起,点标题展开。侧栏面板是它的**汇总视图**,这个视图才是用户真正
|
|
15698
|
+
* 会一直盯着看的东西。
|
|
15699
|
+
*
|
|
15700
|
+
* ## 三个阶段的读法
|
|
15701
|
+
*
|
|
15702
|
+
* `tool.call.toolview` 的 `block` 是阶段相关的(平台在 harness 0.1.7-rc.1 之后
|
|
15703
|
+
* 交来 preparing / start / result 三态):
|
|
15704
|
+
* - `phase: 'preparing'` —— 参数还在流式生成,只有前缀视图,没有 `argsRaw`;
|
|
15705
|
+
* - `phase: 'start'` —— 参数完整(`argsRaw`),结果还没回来;
|
|
15706
|
+
* - `'kind' in block`(`tool-result`)—— 已结算,`isError` 决定是成功还是失败。
|
|
15707
|
+
*
|
|
15708
|
+
* 参数三个阶段的读法不同,所以 {@link argOf} 先试前缀视图(`args.text`),再退回
|
|
15709
|
+
* `argsRaw` 的 JSON 解析——与同目录的 `CodexImageToolView.promptFromBlock` 同款,
|
|
15710
|
+
* 因为它要面对的正是同一批阶段形状。
|
|
15711
|
+
*
|
|
15712
|
+
* 组件只读它真正用到的几个字段,不写 `PropsRuntime<'tool.call.toolview'>`:那个
|
|
15713
|
+
* 类型的 owner 面由 ui-tool 合并,本插件不依赖它;反过来,写窄一点也让这份视图
|
|
15714
|
+
* 能在没有槽位框架的场合(jsdom 测试)直接渲染。
|
|
15715
|
+
*
|
|
15716
|
+
* @module @eddyskywalker/dsh-chatgpt-subscription/client/progress-updates/progress-row
|
|
15717
|
+
*/
|
|
15718
|
+
/** 状态点的颜色:运行中品牌色、成功绿、失败红。 */
|
|
15719
|
+
const DOT_COLORS = {
|
|
15720
|
+
running: "var(--dsw-alias-brand-primary, #4d6bfe)",
|
|
15721
|
+
done: "var(--dsw-alias-state-success-primary, #2ba471)",
|
|
15722
|
+
error: "var(--dsw-alias-state-error-primary, #d54941)"
|
|
15723
|
+
};
|
|
15724
|
+
/** 状态对应的文案键。 */
|
|
15725
|
+
const STATE_KEYS = {
|
|
15726
|
+
running: "rowRunning",
|
|
15727
|
+
done: "rowDone",
|
|
15728
|
+
error: "rowError"
|
|
15729
|
+
};
|
|
15730
|
+
/** 行的外框样式。 */
|
|
15731
|
+
const rowStyle = {
|
|
15732
|
+
display: "flex",
|
|
15733
|
+
flexDirection: "column",
|
|
15734
|
+
gap: "6px",
|
|
15735
|
+
width: "100%",
|
|
15736
|
+
fontSize: "12px",
|
|
15737
|
+
color: "var(--dsw-alias-label-primary, #1f2329)"
|
|
15738
|
+
};
|
|
15739
|
+
/** 标题行按钮样式(整行可点,去掉原生按钮外观)。 */
|
|
15740
|
+
const headerStyle = {
|
|
15741
|
+
display: "flex",
|
|
15742
|
+
alignItems: "baseline",
|
|
15743
|
+
gap: "8px",
|
|
15744
|
+
width: "100%",
|
|
15745
|
+
padding: "0",
|
|
15746
|
+
border: "0",
|
|
15747
|
+
background: "transparent",
|
|
15748
|
+
color: "inherit",
|
|
15749
|
+
font: "inherit",
|
|
15750
|
+
textAlign: "left",
|
|
15751
|
+
cursor: "pointer"
|
|
15752
|
+
};
|
|
15753
|
+
/** 标题文本样式。 */
|
|
15754
|
+
const titleStyle = {
|
|
15755
|
+
flex: "1 1 auto",
|
|
15756
|
+
minWidth: "0",
|
|
15757
|
+
fontSize: "12px",
|
|
15758
|
+
fontWeight: 500,
|
|
15759
|
+
lineHeight: "1.5",
|
|
15760
|
+
overflowWrap: "anywhere"
|
|
15761
|
+
};
|
|
15762
|
+
/** 状态词的小字样式。 */
|
|
15763
|
+
const stateStyle = {
|
|
15764
|
+
flex: "0 0 auto",
|
|
15765
|
+
fontSize: "11px",
|
|
15766
|
+
lineHeight: "1.5",
|
|
15767
|
+
color: "var(--dsw-alias-label-tertiary, #8a9099)"
|
|
15768
|
+
};
|
|
15769
|
+
/** 正文样式:保留换行(契约允许轻 Markdown,这里只保证换行可读)。 */
|
|
15770
|
+
const messageStyle = {
|
|
15771
|
+
whiteSpace: "pre-wrap",
|
|
15772
|
+
overflowWrap: "anywhere",
|
|
15773
|
+
margin: "0",
|
|
15774
|
+
fontSize: "12px",
|
|
15775
|
+
lineHeight: "1.55",
|
|
15776
|
+
color: "var(--dsw-alias-label-secondary, #5b6270)"
|
|
15777
|
+
};
|
|
15778
|
+
/**
|
|
15779
|
+
* 阶段判定:`'kind' in block` 即已结算(与官方视图同一条判据)。
|
|
15780
|
+
* @param block - 当前阶段的内容块。
|
|
15781
|
+
* @returns 是否已结算。
|
|
15782
|
+
*/
|
|
15783
|
+
function isSettled(block) {
|
|
15784
|
+
return block.kind !== void 0;
|
|
15785
|
+
}
|
|
15786
|
+
/**
|
|
15787
|
+
* 读一个字符串参数:先试前缀视图,再退回完整参数 JSON。
|
|
15788
|
+
* @param block - 当前阶段的内容块。
|
|
15789
|
+
* @param key - 参数名。
|
|
15790
|
+
* @returns 读到就返回文本;都没有时 undefined。
|
|
15791
|
+
*/
|
|
15792
|
+
function argOf(block, key) {
|
|
15793
|
+
const read = block.args?.text;
|
|
15794
|
+
if (typeof read === "function") try {
|
|
15795
|
+
const value = read.call(block.args, key);
|
|
15796
|
+
if (typeof value === "string") return value;
|
|
15797
|
+
} catch {}
|
|
15798
|
+
if (typeof block.argsRaw !== "string" || block.argsRaw.trim() === "") return void 0;
|
|
15799
|
+
try {
|
|
15800
|
+
const parsed = JSON.parse(block.argsRaw);
|
|
15801
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return void 0;
|
|
15802
|
+
const value = parsed[key];
|
|
15803
|
+
return typeof value === "string" ? value : void 0;
|
|
15804
|
+
} catch {
|
|
15805
|
+
return;
|
|
15806
|
+
}
|
|
15807
|
+
}
|
|
15808
|
+
/**
|
|
15809
|
+
* 把一次工具调用折成一行进展。
|
|
15810
|
+
* @param block - 当前阶段的内容块。
|
|
15811
|
+
* @returns 进展形状(id/时间戳不在参数里,故只取标题与正文)。
|
|
15812
|
+
*/
|
|
15813
|
+
function progressOfBlock(block) {
|
|
15814
|
+
const message = argOf(block, "message") ?? "";
|
|
15815
|
+
const title = argOf(block, "title");
|
|
15816
|
+
return {
|
|
15817
|
+
message,
|
|
15818
|
+
...title === void 0 || title.trim() === "" ? {} : { title: title.trim() }
|
|
15819
|
+
};
|
|
15820
|
+
}
|
|
15821
|
+
/**
|
|
15822
|
+
* 对话流里的一行进展。
|
|
15823
|
+
* @param props - 内容块与文案座位。
|
|
15824
|
+
* @returns 一行进展;标题行常显,正文按交互展开。
|
|
15825
|
+
*/
|
|
15826
|
+
function ProgressRow({ block, t }) {
|
|
15827
|
+
const translate = (key) => t === void 0 ? fallbackTranslate(key) : t(key);
|
|
15828
|
+
const state = isSettled(block) ? block.isError === true ? "error" : "done" : "running";
|
|
15829
|
+
const { title, message } = progressOfBlock(block);
|
|
15830
|
+
const heading = title ?? message.split("\n").find((line) => line.trim() !== "") ?? translate("rowPending");
|
|
15831
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
15832
|
+
const label = translate(expanded ? "rowCollapse" : "rowExpand");
|
|
15833
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15834
|
+
"data-dsh-plugin": "progress-updates",
|
|
15835
|
+
"data-dsh-part": "progress-row",
|
|
15836
|
+
"data-state": state,
|
|
15837
|
+
style: rowStyle,
|
|
15838
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
15839
|
+
type: "button",
|
|
15840
|
+
"data-dsh-part": "progress-row-header",
|
|
15841
|
+
"aria-expanded": expanded,
|
|
15842
|
+
"aria-label": label,
|
|
15843
|
+
title: label,
|
|
15844
|
+
style: headerStyle,
|
|
15845
|
+
onClick: () => {
|
|
15846
|
+
setExpanded((open) => !open);
|
|
15847
|
+
},
|
|
15848
|
+
children: [
|
|
15849
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15850
|
+
"aria-hidden": "true",
|
|
15851
|
+
"data-dsh-part": "progress-row-dot",
|
|
15852
|
+
style: {
|
|
15853
|
+
flex: "0 0 auto",
|
|
15854
|
+
width: "6px",
|
|
15855
|
+
height: "6px",
|
|
15856
|
+
borderRadius: "50%",
|
|
15857
|
+
background: DOT_COLORS[state]
|
|
15858
|
+
}
|
|
15859
|
+
}),
|
|
15860
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15861
|
+
"data-dsh-part": "progress-row-title",
|
|
15862
|
+
style: titleStyle,
|
|
15863
|
+
children: heading
|
|
15864
|
+
}),
|
|
15865
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15866
|
+
"data-dsh-part": "progress-row-state",
|
|
15867
|
+
style: stateStyle,
|
|
15868
|
+
children: translate(STATE_KEYS[state])
|
|
15869
|
+
})
|
|
15870
|
+
]
|
|
15871
|
+
}), expanded && message !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
15872
|
+
"data-dsh-part": "progress-row-message",
|
|
15873
|
+
style: messageStyle,
|
|
15874
|
+
children: message
|
|
15875
|
+
})]
|
|
15876
|
+
});
|
|
15877
|
+
}
|
|
15878
|
+
/**
|
|
15879
|
+
* 没有表单通道时的空快照。写成模块级常量,是因为
|
|
15880
|
+
* `useSyncExternalStore` 要求 `getSnapshot` 返回稳定引用。
|
|
15881
|
+
*/
|
|
15882
|
+
const NO_SETTINGS_SNAPSHOT = Object.freeze({
|
|
15883
|
+
status: "unavailable",
|
|
15884
|
+
value: void 0,
|
|
15885
|
+
base: void 0,
|
|
15886
|
+
user: void 0,
|
|
15887
|
+
revision: void 0,
|
|
15888
|
+
writable: false
|
|
15889
|
+
});
|
|
15890
|
+
/** 没有表单通道时的订阅(永不回调)。 */
|
|
15891
|
+
const NO_SETTINGS_SUBSCRIBE = () => () => {};
|
|
15892
|
+
/** 没有表单通道时的快照读取。 */
|
|
15893
|
+
const NO_SETTINGS_READ = () => NO_SETTINGS_SNAPSHOT;
|
|
15894
|
+
/** 行尾 chevron,与 hub 卡片同一个形状(14px、1.5 描边)。 */
|
|
15895
|
+
function Chevron() {
|
|
15896
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
15897
|
+
viewBox: "0 0 16 16",
|
|
15898
|
+
width: "14",
|
|
15899
|
+
height: "14",
|
|
15900
|
+
fill: "none",
|
|
15901
|
+
stroke: "currentColor",
|
|
15902
|
+
strokeWidth: "1.5",
|
|
15903
|
+
strokeLinecap: "round",
|
|
15904
|
+
strokeLinejoin: "round",
|
|
15905
|
+
"aria-hidden": "true",
|
|
15906
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M6 4l4 4-4 4" })
|
|
15907
|
+
});
|
|
15908
|
+
}
|
|
15909
|
+
/** 第一行的标记:一条带箭头的进展线。 */
|
|
15910
|
+
function ProgressMark() {
|
|
15911
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
15912
|
+
viewBox: "0 0 20 20",
|
|
15913
|
+
width: "20",
|
|
15914
|
+
height: "20",
|
|
15915
|
+
fill: "none",
|
|
15916
|
+
stroke: "currentColor",
|
|
15917
|
+
strokeWidth: "1.6",
|
|
15918
|
+
strokeLinecap: "round",
|
|
15919
|
+
strokeLinejoin: "round",
|
|
15920
|
+
"aria-hidden": "true",
|
|
15921
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M4 14.5 8.5 10l3 3L16 8" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12.4 8H16v3.6" })]
|
|
15922
|
+
});
|
|
15923
|
+
}
|
|
15924
|
+
/** 第二行的标记:三条阈值刻度。 */
|
|
15925
|
+
function ThresholdMark() {
|
|
15926
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
15927
|
+
viewBox: "0 0 20 20",
|
|
15928
|
+
width: "20",
|
|
15929
|
+
height: "20",
|
|
15930
|
+
fill: "none",
|
|
15931
|
+
stroke: "currentColor",
|
|
15932
|
+
strokeWidth: "1.6",
|
|
15933
|
+
strokeLinecap: "round",
|
|
15934
|
+
strokeLinejoin: "round",
|
|
15935
|
+
"aria-hidden": "true",
|
|
15936
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M4 6h12M4 10h8M4 14h5" })
|
|
15937
|
+
});
|
|
15938
|
+
}
|
|
15939
|
+
/** 第三行的标记:一块右侧栏面板。 */
|
|
15940
|
+
function PanelMark() {
|
|
15941
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
15942
|
+
viewBox: "0 0 20 20",
|
|
15943
|
+
width: "20",
|
|
15944
|
+
height: "20",
|
|
15945
|
+
fill: "none",
|
|
15946
|
+
stroke: "currentColor",
|
|
15947
|
+
strokeWidth: "1.6",
|
|
15948
|
+
strokeLinecap: "round",
|
|
15949
|
+
strokeLinejoin: "round",
|
|
15950
|
+
"aria-hidden": "true",
|
|
15951
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("rect", {
|
|
15952
|
+
x: "3",
|
|
15953
|
+
y: "4.5",
|
|
15954
|
+
width: "14",
|
|
15955
|
+
height: "11",
|
|
15956
|
+
rx: "2"
|
|
15957
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M12 4.5v11" })]
|
|
15958
|
+
});
|
|
15959
|
+
}
|
|
15960
|
+
/**
|
|
15961
|
+
* 一行卡片的外壳:左侧方块、中间名称与说明、右侧控件。
|
|
15962
|
+
* @param props - 行内各部件与是否关闭态。
|
|
15963
|
+
* @returns 一行设置。
|
|
15964
|
+
*/
|
|
15965
|
+
function CardRow(props) {
|
|
15966
|
+
const { part, brand, mark, name, note, danger, enabled, children } = props;
|
|
15967
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
15968
|
+
className: "dsh-progress-card",
|
|
15969
|
+
"data-dsh-part": part,
|
|
15970
|
+
"data-enabled": enabled === false ? "false" : "true",
|
|
15971
|
+
children: [
|
|
15972
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15973
|
+
className: "dsh-progress-brand-tile",
|
|
15974
|
+
"aria-hidden": "true",
|
|
15975
|
+
style: {
|
|
15976
|
+
width: 40,
|
|
15977
|
+
height: 40,
|
|
15978
|
+
["--dsh-progress-brand"]: brand
|
|
15979
|
+
},
|
|
15980
|
+
children: mark
|
|
15981
|
+
}),
|
|
15982
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
15983
|
+
className: "dsh-progress-card-copy",
|
|
15984
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15985
|
+
className: "dsh-progress-card-name",
|
|
15986
|
+
children: name
|
|
15987
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15988
|
+
className: danger === true ? "dsh-progress-card-note dsh-progress-note-danger" : "dsh-progress-card-note",
|
|
15989
|
+
children: note
|
|
15990
|
+
})]
|
|
15991
|
+
}),
|
|
15992
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
15993
|
+
className: "dsh-progress-card-side",
|
|
15994
|
+
children
|
|
15995
|
+
})
|
|
15996
|
+
]
|
|
15997
|
+
});
|
|
15998
|
+
}
|
|
15999
|
+
/**
|
|
16000
|
+
* 进展更新的设置分区:三行卡片。
|
|
16001
|
+
* @param props - 壳的关闭回调、注入的打开动作与表单、文案座位。
|
|
16002
|
+
* @returns 设置页里的这一页。
|
|
16003
|
+
*/
|
|
16004
|
+
function ProgressUpdatesSettingsSection(props) {
|
|
16005
|
+
const { openPanel, close, settings, t } = props;
|
|
16006
|
+
const translate = (key) => t === void 0 ? fallbackTranslate(key) : t(key);
|
|
16007
|
+
const snapshot = (0, react.useSyncExternalStore)(settings?.subscribe ?? NO_SETTINGS_SUBSCRIBE, settings?.getSnapshot ?? NO_SETTINGS_READ, settings?.getSnapshot ?? NO_SETTINGS_READ);
|
|
16008
|
+
/** 写入被拒/失败时落在哪一行、说什么。 */
|
|
16009
|
+
const [failure, setFailure] = (0, react.useState)(void 0);
|
|
16010
|
+
const value = snapshot.value ?? {};
|
|
16011
|
+
const writable = settings !== void 0 && snapshot.status === "ready" && snapshot.writable;
|
|
16012
|
+
const enabled = value.progressUpdates === true;
|
|
16013
|
+
const threshold = typeof value.progressUpdatesNudgeThreshold === "number" ? value.progressUpdatesNudgeThreshold : 8;
|
|
16014
|
+
const [draft, setDraft] = (0, react.useState)(() => String(threshold));
|
|
16015
|
+
(0, react.useEffect)(() => {
|
|
16016
|
+
setDraft(String(threshold));
|
|
16017
|
+
}, [threshold]);
|
|
16018
|
+
/**
|
|
16019
|
+
* 写一个字段;宿主拒绝、传输失败、没有通道都落到该行的说明行上。
|
|
16020
|
+
* @param row - 出问题的是哪一行。
|
|
16021
|
+
* @param field - 字段名(本插件 Config 的根字段)。
|
|
16022
|
+
* @param next - 新值。
|
|
16023
|
+
*/
|
|
16024
|
+
const write = (row, field, next) => {
|
|
16025
|
+
setFailure(void 0);
|
|
16026
|
+
if (settings === void 0) {
|
|
16027
|
+
setFailure({
|
|
16028
|
+
row,
|
|
16029
|
+
text: translate("settingsUnavailable")
|
|
16030
|
+
});
|
|
16031
|
+
return;
|
|
16032
|
+
}
|
|
16033
|
+
let answer;
|
|
16034
|
+
try {
|
|
16035
|
+
answer = settings.set(field, next);
|
|
16036
|
+
} catch (error) {
|
|
16037
|
+
setFailure({
|
|
16038
|
+
row,
|
|
16039
|
+
text: error instanceof Error ? error.message : String(error)
|
|
16040
|
+
});
|
|
16041
|
+
return;
|
|
16042
|
+
}
|
|
16043
|
+
Promise.resolve(answer).then((accepted) => {
|
|
16044
|
+
if (!accepted) setFailure({
|
|
16045
|
+
row,
|
|
16046
|
+
text: translate("settingsSaveFailed")
|
|
16047
|
+
});
|
|
16048
|
+
}, (error) => {
|
|
16049
|
+
setFailure({
|
|
16050
|
+
row,
|
|
16051
|
+
text: error instanceof Error ? error.message : String(error)
|
|
16052
|
+
});
|
|
16053
|
+
});
|
|
16054
|
+
};
|
|
16055
|
+
/**
|
|
16056
|
+
* 阈值输入:只有落进 1..50 的整数才提交,半截输入不打扰宿主。
|
|
16057
|
+
* @param raw - 输入框的原始文本。
|
|
16058
|
+
*/
|
|
16059
|
+
const editThreshold = (raw) => {
|
|
16060
|
+
setDraft(raw);
|
|
16061
|
+
const parsed = Number(raw);
|
|
16062
|
+
if (!Number.isSafeInteger(parsed) || parsed < 1 || parsed > 50) return;
|
|
16063
|
+
write("threshold", "progressUpdatesNudgeThreshold", parsed);
|
|
16064
|
+
};
|
|
16065
|
+
/** 打开面板再收起设置。 */
|
|
16066
|
+
const open = () => {
|
|
16067
|
+
try {
|
|
16068
|
+
openPanel();
|
|
16069
|
+
} catch {}
|
|
16070
|
+
try {
|
|
16071
|
+
close();
|
|
16072
|
+
} catch {}
|
|
16073
|
+
};
|
|
16074
|
+
const unavailable = settings === void 0 ? translate("settingsUnavailable") : void 0;
|
|
16075
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16076
|
+
className: "dsh-progress-overview",
|
|
16077
|
+
"data-dsh-plugin": "progress-updates",
|
|
16078
|
+
"data-dsh-part": "settings",
|
|
16079
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
16080
|
+
className: "dsh-progress-hint",
|
|
16081
|
+
"data-dsh-part": "settings-hint",
|
|
16082
|
+
children: translate("settingsHint")
|
|
16083
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
16084
|
+
className: "dsh-progress-cards",
|
|
16085
|
+
children: [
|
|
16086
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardRow, {
|
|
16087
|
+
part: "settings-row-enabled",
|
|
16088
|
+
brand: "#4d6bfe",
|
|
16089
|
+
mark: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProgressMark, {}),
|
|
16090
|
+
name: translate("settingsEnabledName"),
|
|
16091
|
+
note: unavailable ?? (failure?.row === "enabled" ? failure.text : translate("settingsEnabledNote")),
|
|
16092
|
+
danger: failure?.row === "enabled" || unavailable !== void 0,
|
|
16093
|
+
enabled,
|
|
16094
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16095
|
+
type: "button",
|
|
16096
|
+
role: "switch",
|
|
16097
|
+
"aria-checked": enabled,
|
|
16098
|
+
"aria-label": translate("settingsEnabledName"),
|
|
16099
|
+
className: "dsh-progress-switch",
|
|
16100
|
+
"data-dsh-part": "settings-switch",
|
|
16101
|
+
disabled: !writable,
|
|
16102
|
+
onClick: () => {
|
|
16103
|
+
write("enabled", "progressUpdates", !enabled);
|
|
16104
|
+
}
|
|
16105
|
+
})
|
|
16106
|
+
}),
|
|
16107
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CardRow, {
|
|
16108
|
+
part: "settings-row-threshold",
|
|
16109
|
+
brand: "#2f9e6e",
|
|
16110
|
+
mark: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ThresholdMark, {}),
|
|
16111
|
+
name: translate("settingsThresholdName"),
|
|
16112
|
+
note: failure?.row === "threshold" ? failure.text : translate("settingsThresholdNote"),
|
|
16113
|
+
danger: failure?.row === "threshold",
|
|
16114
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
16115
|
+
type: "number",
|
|
16116
|
+
inputMode: "numeric",
|
|
16117
|
+
min: 1,
|
|
16118
|
+
max: 50,
|
|
16119
|
+
step: 1,
|
|
16120
|
+
className: "dsh-progress-number",
|
|
16121
|
+
"data-dsh-part": "settings-threshold",
|
|
16122
|
+
"aria-label": translate("settingsThresholdName"),
|
|
16123
|
+
value: draft,
|
|
16124
|
+
disabled: !writable,
|
|
16125
|
+
onChange: (event) => {
|
|
16126
|
+
editThreshold(event.target.value);
|
|
16127
|
+
},
|
|
16128
|
+
onBlur: () => {
|
|
16129
|
+
setDraft(String(threshold));
|
|
16130
|
+
}
|
|
16131
|
+
})
|
|
16132
|
+
}),
|
|
16133
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(CardRow, {
|
|
16134
|
+
part: "settings-row-panel",
|
|
16135
|
+
brand: "#7c6aef",
|
|
16136
|
+
mark: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PanelMark, {}),
|
|
16137
|
+
name: translate("settingsPanelName"),
|
|
16138
|
+
note: translate("settingsPanelNote"),
|
|
16139
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
16140
|
+
type: "button",
|
|
16141
|
+
className: "dsh-progress-open",
|
|
16142
|
+
"data-dsh-part": "settings-open",
|
|
16143
|
+
onClick: open,
|
|
16144
|
+
children: translate("settingsOpen")
|
|
16145
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
16146
|
+
className: "dsh-progress-chevron",
|
|
16147
|
+
"aria-hidden": "true",
|
|
16148
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Chevron, {})
|
|
16149
|
+
})]
|
|
16150
|
+
})
|
|
16151
|
+
]
|
|
16152
|
+
})]
|
|
16153
|
+
});
|
|
16154
|
+
}
|
|
16155
|
+
//#endregion
|
|
16156
|
+
//#region src/client/progress-updates/tool-name.ts
|
|
16157
|
+
/**
|
|
16158
|
+
* 客户端半边的工具名常量。
|
|
16159
|
+
*
|
|
16160
|
+
* 不能直接从 `../../host/progress-updates/tool.ts` 取 `NOTIFY_USER_TOOL`:那一侧
|
|
16161
|
+
* import 了 `@deepseek-ai/dsh-tools` 的 `defineTool`,浏览器闭包包拉进宿主工具
|
|
16162
|
+
* 注册表既无意义也违反客户端的模块纯度纪律(客户端 tsconfig 的 include 也不含
|
|
16163
|
+
* `src/host/**`)。这里按同一份冻结契约再写一遍,由
|
|
16164
|
+
* `test/progress-updates-client.test.tsx` 断言它与宿主侧逐字相同。
|
|
16165
|
+
*
|
|
16166
|
+
* @module @eddyskywalker/dsh-chatgpt-subscription/client/progress-updates/tool-name
|
|
16167
|
+
*/
|
|
16168
|
+
/** 注册的工具名(与 `../../host/progress-updates/tool.ts` 的 `NOTIFY_USER_TOOL` 同一冻结契约)。 */
|
|
16169
|
+
const PROGRESS_UPDATE_TOOL_NAME = "progress_update";
|
|
16170
|
+
//#endregion
|
|
16171
|
+
//#region src/client/progress-updates/host-source.ts
|
|
16172
|
+
/**
|
|
16173
|
+
* 本路由族的前缀(**文档相对**,不带前导斜杠)。
|
|
16174
|
+
*
|
|
16175
|
+
* 宿主那边注册的是绝对路径 `/api/dsh-progress-updates/updates`;浏览器这边必须写
|
|
16176
|
+
* 成不带前导斜杠的相对路径:GUI 以 `<base href="./">` 提供,子路径部署时相对路径
|
|
16177
|
+
* 才会解析到该部署的入口目录下,前导斜杠会逃逸出这个前缀、永远打不到路由
|
|
16178
|
+
* (同 dsh-usage / dsh-git-graph 的处理)。
|
|
16179
|
+
*/
|
|
16180
|
+
const PROGRESS_UPDATES_PATH = "api/dsh-progress-updates/updates";
|
|
16181
|
+
/** 默认定时器:直接转发到 `globalThis`(浏览器与 Node 都能跑)。 */
|
|
16182
|
+
const DEFAULT_TIMERS = {
|
|
16183
|
+
setInterval: (handler, ms) => globalThis.setInterval(handler, ms),
|
|
16184
|
+
clearInterval: (handle) => {
|
|
16185
|
+
globalThis.clearInterval(handle);
|
|
16186
|
+
},
|
|
16187
|
+
setTimeout: (handler, ms) => globalThis.setTimeout(handler, ms),
|
|
16188
|
+
clearTimeout: (handle) => {
|
|
16189
|
+
globalThis.clearTimeout(handle);
|
|
16190
|
+
}
|
|
16191
|
+
};
|
|
16192
|
+
/**
|
|
16193
|
+
* 判断页面当前是否可见。
|
|
16194
|
+
*
|
|
16195
|
+
* 只在明确拿到 `hidden` 时判为不可见:恒为 `prerender` 的嵌入式 WebView、或者
|
|
16196
|
+
* 压根没有 `document` 的运行环境都会落到"可见"这一侧 —— **退化行为是继续按固定
|
|
16197
|
+
* 间隔轮询**,而不是静默停摆(面板停摆比多轮询几次糟糕得多)。
|
|
16198
|
+
* @returns 是否应该继续轮询。
|
|
16199
|
+
*/
|
|
16200
|
+
function isDocumentVisible() {
|
|
16201
|
+
try {
|
|
16202
|
+
if (typeof document === "undefined") return true;
|
|
16203
|
+
return document.visibilityState !== "hidden";
|
|
16204
|
+
} catch {
|
|
16205
|
+
return true;
|
|
16206
|
+
}
|
|
16207
|
+
}
|
|
16208
|
+
/**
|
|
16209
|
+
* 把路由响应体解成进展数组。
|
|
16210
|
+
*
|
|
16211
|
+
* 形状不对就当空列表,而**不是**当失败:宿主回了"当前为空"也要如实显示空态。
|
|
16212
|
+
* @param body - 解析后的 JSON。
|
|
16213
|
+
* @returns 进展数组。
|
|
16214
|
+
*/
|
|
16215
|
+
function decodeUpdates(body) {
|
|
16216
|
+
if (typeof body !== "object" || body === null) return [];
|
|
16217
|
+
const updates = body.updates;
|
|
16218
|
+
if (!Array.isArray(updates)) return [];
|
|
16219
|
+
return updates.filter((item) => {
|
|
16220
|
+
if (typeof item !== "object" || item === null) return false;
|
|
16221
|
+
const record = item;
|
|
16222
|
+
return typeof record.id === "string" && typeof record.message === "string" && typeof record.at === "number";
|
|
16223
|
+
});
|
|
16224
|
+
}
|
|
16225
|
+
/**
|
|
16226
|
+
* 拼出这次请求的地址。
|
|
16227
|
+
*
|
|
16228
|
+
* 拿得到会话 id 就带上 `?session=<id>`(宿主按会话分桶);拿不到(槽位没有把
|
|
16229
|
+
* sessionId 传下来)就**退化到不带参数**的请求 —— 宿主对空 session 的语义是
|
|
16230
|
+
* "当前/合并桶"(`src/host/progress-updates/index.ts` 的 `updatesOf('')` 回退到
|
|
16231
|
+
* `session` 桶),因此单会话部署下仍能拿到进展。
|
|
16232
|
+
* @param sessionId - 当前会话 id,空串/undefined 表示退化。
|
|
16233
|
+
* @returns 文档相对的请求地址。
|
|
16234
|
+
*/
|
|
16235
|
+
function progressUpdatesUrl(sessionId) {
|
|
16236
|
+
if (sessionId === void 0 || sessionId === "") return PROGRESS_UPDATES_PATH;
|
|
16237
|
+
return `${PROGRESS_UPDATES_PATH}?session=${encodeURIComponent(sessionId)}`;
|
|
16238
|
+
}
|
|
16239
|
+
/**
|
|
16240
|
+
* 两份快照内容是否相同(逐条比 id / 标题 / 正文 / 时间戳 / 来源)。
|
|
16241
|
+
*
|
|
16242
|
+
* 相同就沿用旧引用:无意义的通知会让整棵面板重渲染,也会让
|
|
16243
|
+
* `useSyncExternalStore` 白白跑一轮比较。
|
|
16244
|
+
* @param left - 上一份快照。
|
|
16245
|
+
* @param right - 这次拿到的快照。
|
|
16246
|
+
* @returns 内容是否一致。
|
|
16247
|
+
*/
|
|
16248
|
+
function sameList(left, right) {
|
|
16249
|
+
if (left === right) return true;
|
|
16250
|
+
if (left.length !== right.length) return false;
|
|
16251
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
16252
|
+
const a = left[index];
|
|
16253
|
+
const b = right[index];
|
|
16254
|
+
if (a.id !== b.id || a.message !== b.message || a.at !== b.at || a.title !== b.title || a.source !== b.source) return false;
|
|
16255
|
+
}
|
|
16256
|
+
return true;
|
|
16257
|
+
}
|
|
16258
|
+
/**
|
|
16259
|
+
* 起一个轮询数据源:真正发 `GET` 请求,把结果喂给面板。
|
|
16260
|
+
*
|
|
16261
|
+
* 语义要点:
|
|
16262
|
+
* - 惰性:没有订阅者时不发任何请求(页签关着 = 零流量);
|
|
16263
|
+
* - 稳定引用:两次成功请求之间 `getSnapshot()` 返回**同一个数组对象**,
|
|
16264
|
+
* `useSyncExternalStore` 不会判成无限更新;新快照内容不变时也沿用旧引用;
|
|
16265
|
+
* - 吞错:任何失败都只是"这次没更新",最后一份快照原样保留。
|
|
16266
|
+
*
|
|
16267
|
+
* @param sessionId - 当前会话 id(可能为空:见 {@link progressUpdatesUrl} 的退化说明)。
|
|
16268
|
+
* @param deps - 可注入依赖(fetch、定时器、可见性、间隔)。
|
|
16269
|
+
* @returns 面板数据源 + 显式停止入口。
|
|
16270
|
+
*/
|
|
16271
|
+
function createHostProgressSource(sessionId, deps = {}) {
|
|
16272
|
+
const pollMs = deps.pollMs ?? 2e3;
|
|
16273
|
+
const timeoutMs = deps.timeoutMs ?? 8e3;
|
|
16274
|
+
const timers = deps.timers ?? DEFAULT_TIMERS;
|
|
16275
|
+
const visible = deps.isVisible ?? isDocumentVisible;
|
|
16276
|
+
const decode = deps.decode ?? decodeUpdates;
|
|
16277
|
+
/** 最后一份**成功**快照;面板只认它。失败不会把它清空。 */
|
|
16278
|
+
let snapshot = [];
|
|
16279
|
+
/** 订阅者集合;空集 = 没人看 = 不轮询。 */
|
|
16280
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
16281
|
+
/** 当前轮询定时器句柄。 */
|
|
16282
|
+
let timer = void 0;
|
|
16283
|
+
/** 有没有正在飞的请求:慢宿主上不叠加请求。 */
|
|
16284
|
+
let inFlight = false;
|
|
16285
|
+
/** 每条请求的超时定时器,停止时统一清掉。 */
|
|
16286
|
+
const pendingTimeouts = /* @__PURE__ */ new Set();
|
|
16287
|
+
/** 是否已经被 {@link HostProgressSource.stop} 停掉(终态,不再起表)。 */
|
|
16288
|
+
let stopped = false;
|
|
16289
|
+
const notify = () => {
|
|
16290
|
+
for (const listener of Array.from(listeners)) try {
|
|
16291
|
+
listener();
|
|
16292
|
+
} catch {}
|
|
16293
|
+
};
|
|
16294
|
+
/** 发一次读取请求。永不 reject —— 失败就是"这次没更新"。 */
|
|
16295
|
+
const refresh = async () => {
|
|
16296
|
+
if (inFlight) return;
|
|
16297
|
+
inFlight = true;
|
|
16298
|
+
const controller = new AbortController();
|
|
16299
|
+
const timeoutTimer = timers.setTimeout(() => {
|
|
16300
|
+
controller.abort();
|
|
16301
|
+
}, timeoutMs);
|
|
16302
|
+
pendingTimeouts.add(timeoutTimer);
|
|
16303
|
+
try {
|
|
16304
|
+
const fetchImpl = deps.fetch ?? globalThis.fetch;
|
|
16305
|
+
if (typeof fetchImpl !== "function") throw new Error("fetch unavailable");
|
|
16306
|
+
const response = await fetchImpl(progressUpdatesUrl(sessionId), {
|
|
16307
|
+
method: "GET",
|
|
16308
|
+
headers: { accept: "application/json" },
|
|
16309
|
+
cache: "no-store",
|
|
16310
|
+
signal: controller.signal
|
|
16311
|
+
});
|
|
16312
|
+
if (!response.ok) throw new Error(`progress updates: HTTP ${response.status}`);
|
|
16313
|
+
const body = await response.json();
|
|
16314
|
+
const next = decode(body);
|
|
16315
|
+
if (sameList(snapshot, next)) return;
|
|
16316
|
+
snapshot = Object.freeze([...next]);
|
|
16317
|
+
notify();
|
|
16318
|
+
} catch (error) {
|
|
16319
|
+
deps.onError?.(error);
|
|
16320
|
+
} finally {
|
|
16321
|
+
timers.clearTimeout(timeoutTimer);
|
|
16322
|
+
pendingTimeouts.delete(timeoutTimer);
|
|
16323
|
+
inFlight = false;
|
|
16324
|
+
}
|
|
16325
|
+
};
|
|
16326
|
+
const stopTimer = () => {
|
|
16327
|
+
if (timer === void 0) return;
|
|
16328
|
+
timers.clearInterval(timer);
|
|
16329
|
+
timer = void 0;
|
|
16330
|
+
};
|
|
16331
|
+
const startTimer = () => {
|
|
16332
|
+
if (timer !== void 0) return;
|
|
16333
|
+
timer = timers.setInterval(() => {
|
|
16334
|
+
if (!visible()) {
|
|
16335
|
+
stopTimer();
|
|
16336
|
+
return;
|
|
16337
|
+
}
|
|
16338
|
+
refresh();
|
|
16339
|
+
}, pollMs);
|
|
16340
|
+
};
|
|
16341
|
+
/** 可见性变化:切回来立刻补一次再继续轮询,切走立刻停表。 */
|
|
16342
|
+
const onVisibilityChange = () => {
|
|
16343
|
+
if (listeners.size === 0) return;
|
|
16344
|
+
if (visible()) {
|
|
16345
|
+
refresh();
|
|
16346
|
+
startTimer();
|
|
16347
|
+
} else stopTimer();
|
|
16348
|
+
};
|
|
16349
|
+
const attachVisibility = () => {
|
|
16350
|
+
try {
|
|
16351
|
+
if (typeof document !== "undefined" && typeof document.addEventListener === "function") document.addEventListener("visibilitychange", onVisibilityChange);
|
|
16352
|
+
} catch {}
|
|
16353
|
+
};
|
|
16354
|
+
const detachVisibility = () => {
|
|
16355
|
+
try {
|
|
16356
|
+
if (typeof document !== "undefined" && typeof document.removeEventListener === "function") document.removeEventListener("visibilitychange", onVisibilityChange);
|
|
16357
|
+
} catch {}
|
|
16358
|
+
};
|
|
16359
|
+
const subscribe = (listener) => {
|
|
16360
|
+
if (stopped) {
|
|
16361
|
+
listeners.add(listener);
|
|
16362
|
+
return () => {
|
|
16363
|
+
listeners.delete(listener);
|
|
16364
|
+
};
|
|
16365
|
+
}
|
|
16366
|
+
const isFirst = listeners.size === 0;
|
|
16367
|
+
listeners.add(listener);
|
|
16368
|
+
if (isFirst) {
|
|
16369
|
+
refresh();
|
|
16370
|
+
if (visible()) startTimer();
|
|
16371
|
+
attachVisibility();
|
|
16372
|
+
}
|
|
16373
|
+
return () => {
|
|
16374
|
+
listeners.delete(listener);
|
|
16375
|
+
if (listeners.size > 0) return;
|
|
16376
|
+
stopTimer();
|
|
16377
|
+
detachVisibility();
|
|
16378
|
+
};
|
|
16379
|
+
};
|
|
16380
|
+
return {
|
|
16381
|
+
getSnapshot: () => snapshot,
|
|
16382
|
+
subscribe,
|
|
16383
|
+
stop: () => {
|
|
16384
|
+
stopped = true;
|
|
16385
|
+
stopTimer();
|
|
16386
|
+
detachVisibility();
|
|
16387
|
+
listeners.clear();
|
|
16388
|
+
inFlight = false;
|
|
16389
|
+
for (const pending of pendingTimeouts) timers.clearTimeout(pending);
|
|
16390
|
+
pendingTimeouts.clear();
|
|
16391
|
+
}
|
|
16392
|
+
};
|
|
16393
|
+
}
|
|
16394
|
+
//#endregion
|
|
16395
|
+
//#region src/client/progress-updates/settings-binding.ts
|
|
16396
|
+
/**
|
|
16397
|
+
* 候选条目 id,按可能性从高到低。
|
|
16398
|
+
*
|
|
16399
|
+
* 第一项来自 `cordis.patch.yml` 的行 id(独立安装的唯一形态);后两项是聚合安装
|
|
16400
|
+
* 可能套的前缀。镜像答上来之后不再用这张表的顺序,改用"值里带 `progressUpdates`
|
|
16401
|
+
* 的那一条"。
|
|
16402
|
+
*/
|
|
16403
|
+
const PROGRESS_SETTINGS_ENTRY_IDS = [
|
|
16404
|
+
"dsh-chatgpt-subscription",
|
|
16405
|
+
"web-ui-chatgpt-subscription",
|
|
16406
|
+
"chatgpt-subscription"
|
|
16407
|
+
];
|
|
16408
|
+
/** 还没拿到宿主答案时的快照。 */
|
|
16409
|
+
const PENDING = Object.freeze({
|
|
16410
|
+
status: "loading",
|
|
16411
|
+
value: void 0,
|
|
16412
|
+
base: void 0,
|
|
16413
|
+
user: void 0,
|
|
16414
|
+
revision: void 0,
|
|
16415
|
+
writable: false
|
|
16416
|
+
});
|
|
16417
|
+
/**
|
|
16418
|
+
* 读一个服务,读不到就当作没有。
|
|
16419
|
+
* @param ctx - 浏览器根上下文(只用到 `get`)。
|
|
16420
|
+
* @param name - 服务名。
|
|
16421
|
+
* @returns 服务实例或 undefined。
|
|
16422
|
+
*/
|
|
16423
|
+
function serviceOf$1(ctx, name) {
|
|
16424
|
+
try {
|
|
16425
|
+
return ctx.get(name);
|
|
16426
|
+
} catch {
|
|
16427
|
+
return;
|
|
16428
|
+
}
|
|
16429
|
+
}
|
|
16430
|
+
/**
|
|
16431
|
+
* 判断取到的对象是不是共享设置表单服务。
|
|
16432
|
+
* @param value - 任意服务值。
|
|
16433
|
+
* @returns 是否具备 get / describe 两个成员。
|
|
16434
|
+
*/
|
|
16435
|
+
function isConfigForms(value) {
|
|
16436
|
+
if (typeof value !== "object" || value === null) return false;
|
|
16437
|
+
const face = value;
|
|
16438
|
+
return typeof face.get === "function" && typeof face.describe === "function";
|
|
16439
|
+
}
|
|
16440
|
+
/**
|
|
16441
|
+
* 判断一个条目的解析值是不是本插件的 Config 切片。
|
|
16442
|
+
* @param value - describe 交来的解析值。
|
|
16443
|
+
* @returns 是否带 `progressUpdates` 这个键。
|
|
16444
|
+
*/
|
|
16445
|
+
function isProgressSection(value) {
|
|
16446
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
16447
|
+
return "progressUpdates" in value;
|
|
16448
|
+
}
|
|
16449
|
+
/**
|
|
16450
|
+
* 创建本插件设置卡片的表单。
|
|
16451
|
+
*
|
|
16452
|
+
* 共享表单服务缺席时返回 undefined:调用方据此把开关渲染成只读(禁用),
|
|
16453
|
+
* 而不是造一个点了存不住的假开关。
|
|
16454
|
+
* @param ctx - 浏览器根上下文。
|
|
16455
|
+
* @returns 表单通道,或 undefined(这个部署没有设置服务)。
|
|
16456
|
+
*/
|
|
16457
|
+
function createProgressSettingsForm(ctx) {
|
|
16458
|
+
const forms = serviceOf$1(ctx, "configForms");
|
|
16459
|
+
if (!isConfigForms(forms)) return void 0;
|
|
16460
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
16461
|
+
let boundId;
|
|
16462
|
+
let bound;
|
|
16463
|
+
let offBound;
|
|
16464
|
+
let snapshot = PENDING;
|
|
16465
|
+
/** 重发一次:有绑定就发它的最新快照,没有就发 pending。 */
|
|
16466
|
+
const publish = () => {
|
|
16467
|
+
if (bound !== void 0) snapshot = bound.getSnapshot();
|
|
16468
|
+
for (const listener of Array.from(listeners)) listener();
|
|
16469
|
+
};
|
|
16470
|
+
/**
|
|
16471
|
+
* 镜像当前服务的条目。还没答上来(或拒绝读)时返回 undefined —— 没答上来不等于
|
|
16472
|
+
* 没有,不能当成「这个部署不服务本插件」。
|
|
16473
|
+
* @returns 条目列表或 undefined。
|
|
16474
|
+
*/
|
|
16475
|
+
const servedEntries = () => {
|
|
16476
|
+
try {
|
|
16477
|
+
return forms.describe().getSnapshot().view?.namespaces;
|
|
16478
|
+
} catch {
|
|
16479
|
+
return;
|
|
16480
|
+
}
|
|
16481
|
+
};
|
|
16482
|
+
/**
|
|
16483
|
+
* 解析这次该绑定哪个条目 id。
|
|
16484
|
+
*
|
|
16485
|
+
* 先按解析值认领(值里带 `progressUpdates` 的那一条),认不出来再按候选表顺延。
|
|
16486
|
+
* @returns 条目 id。
|
|
16487
|
+
*/
|
|
16488
|
+
const resolve = () => {
|
|
16489
|
+
const served = servedEntries();
|
|
16490
|
+
if (served === void 0 || served.length === 0) return PROGRESS_SETTINGS_ENTRY_IDS[0];
|
|
16491
|
+
const claimed = served.find((entry) => isProgressSection(entry.value));
|
|
16492
|
+
if (claimed !== void 0) return claimed.ns;
|
|
16493
|
+
return PROGRESS_SETTINGS_ENTRY_IDS.find((id) => served.some((entry) => entry.ns === id)) ?? served[0].ns;
|
|
16494
|
+
};
|
|
16495
|
+
/** 绑定(或重绑)到解析出的条目;id 没变时是空操作。 */
|
|
16496
|
+
const bind = () => {
|
|
16497
|
+
const target = resolve();
|
|
16498
|
+
if (target === boundId) return;
|
|
16499
|
+
offBound?.();
|
|
16500
|
+
offBound = void 0;
|
|
16501
|
+
bound = void 0;
|
|
16502
|
+
let form;
|
|
16503
|
+
try {
|
|
16504
|
+
form = forms.get(target);
|
|
16505
|
+
} catch {
|
|
16506
|
+
boundId = void 0;
|
|
16507
|
+
return;
|
|
16508
|
+
}
|
|
16509
|
+
boundId = target;
|
|
16510
|
+
bound = form;
|
|
16511
|
+
offBound = form.subscribe(() => {
|
|
16512
|
+
publish();
|
|
16513
|
+
});
|
|
16514
|
+
publish();
|
|
16515
|
+
};
|
|
16516
|
+
let offDescribe;
|
|
16517
|
+
try {
|
|
16518
|
+
offDescribe = forms.describe().subscribe(() => {
|
|
16519
|
+
bind();
|
|
16520
|
+
});
|
|
16521
|
+
} catch {}
|
|
16522
|
+
bind();
|
|
16523
|
+
return {
|
|
16524
|
+
getSnapshot: () => snapshot,
|
|
16525
|
+
subscribe: (listener) => {
|
|
16526
|
+
listeners.add(listener);
|
|
16527
|
+
return () => {
|
|
16528
|
+
listeners.delete(listener);
|
|
16529
|
+
};
|
|
16530
|
+
},
|
|
16531
|
+
set: (field, value) => bound?.set(field, value) ?? Promise.resolve(false),
|
|
16532
|
+
dispose: () => {
|
|
16533
|
+
offDescribe?.();
|
|
16534
|
+
offDescribe = void 0;
|
|
16535
|
+
offBound?.();
|
|
16536
|
+
offBound = void 0;
|
|
16537
|
+
bound = void 0;
|
|
16538
|
+
boundId = void 0;
|
|
16539
|
+
listeners.clear();
|
|
16540
|
+
}
|
|
16541
|
+
};
|
|
16542
|
+
}
|
|
16543
|
+
//#endregion
|
|
16544
|
+
//#region src/client/progress-updates/index.ts
|
|
16545
|
+
/**
|
|
16546
|
+
* 本实现在右侧栏页签系统里的身份:它既是阶段一 `sidebarRightTabs` 里的 `id`,
|
|
16547
|
+
* 也是阶段二两处 keyed 槽位注册用的键。
|
|
16548
|
+
*/
|
|
16549
|
+
const PANEL_TAB_ID = "@eddyskywalker/dsh-chatgpt-subscription/progress-updates";
|
|
16550
|
+
/**
|
|
16551
|
+
* 页签类型判别值(`kind`):`ctx.sidebarRight.openTab` 用它点名。语义上是
|
|
16552
|
+
* 「一类页签」,所以与上面的身份分开写。
|
|
16553
|
+
*/
|
|
16554
|
+
const PANEL_TAB_KIND = "progress-updates";
|
|
16555
|
+
/**
|
|
16556
|
+
* 设置页那一格的 id。
|
|
16557
|
+
*
|
|
16558
|
+
* **独立一格**:本插件的订阅服务 hub 注册的是 `subscription-hub`(order 45),
|
|
16559
|
+
* 进展更新不进那一格——用户要求它是一个单独的分区。
|
|
16560
|
+
*/
|
|
16561
|
+
const SETTINGS_SECTION_ID = "progress-updates";
|
|
16562
|
+
/**
|
|
16563
|
+
* 从上下文里读一个可选服务,读不到就当作没有。
|
|
16564
|
+
*
|
|
16565
|
+
* 只有 `get` 是 Cordis 的基础成员;没写进 `inject` 的服务名直接当属性读会抛错,
|
|
16566
|
+
* 而这里要读的 `configForms` / `sidebarRight*` 都是可选的,所以一律走 `get`。
|
|
16567
|
+
* @param ctx - 浏览器根上下文。
|
|
16568
|
+
* @param name - 服务名。
|
|
16569
|
+
* @returns 服务实例或 undefined。
|
|
16570
|
+
*/
|
|
16571
|
+
function serviceOf(ctx, name) {
|
|
16572
|
+
try {
|
|
16573
|
+
return ctx.get(name);
|
|
16574
|
+
} catch {
|
|
16575
|
+
return;
|
|
16576
|
+
}
|
|
16577
|
+
}
|
|
16578
|
+
/**
|
|
16579
|
+
* 解析「打开面板」这一动作。
|
|
16580
|
+
*
|
|
16581
|
+
* 优先用调用方显式给的通道(测试、将来换实现),否则读 `ctx.sidebarRight`,
|
|
16582
|
+
* 都没有时退化成空操作——设置页的按钮永远不该把 GUI 带下水。
|
|
16583
|
+
*
|
|
16584
|
+
* 每次点击都重新读一遍服务:页签服务可能比本插件晚挂载,也可能中途被卸载,
|
|
16585
|
+
* 在 apply 时抓一份闭包会把这两种情况都写成死值。
|
|
16586
|
+
* @param ctx - 浏览器根上下文。
|
|
16587
|
+
* @param explicit - 调用方显式传入的通道。
|
|
16588
|
+
* @returns 打开面板的动作。
|
|
16589
|
+
*/
|
|
16590
|
+
function openPanelAction(ctx, explicit) {
|
|
16591
|
+
return () => {
|
|
16592
|
+
if (explicit !== void 0) {
|
|
16593
|
+
explicit(PANEL_TAB_KIND);
|
|
16594
|
+
return;
|
|
16595
|
+
}
|
|
16596
|
+
const sidebarRight = serviceOf(ctx, "sidebarRight");
|
|
16597
|
+
if (typeof sidebarRight?.openTab !== "function") return;
|
|
16598
|
+
sidebarRight.openTab(PANEL_TAB_KIND);
|
|
16599
|
+
};
|
|
16600
|
+
}
|
|
16601
|
+
/**
|
|
16602
|
+
* 挂载「进展更新」的浏览器半边。
|
|
16603
|
+
*
|
|
16604
|
+
* 失败策略与家族其它模块一致:槽位/页签注册失败只吞掉、不抛出 —— web shell 在
|
|
16605
|
+
* 插件 apply 抛错时会整页启动失败,外部插件不能把 GUI 带下水。
|
|
16606
|
+
* @param ctx - 浏览器根上下文。
|
|
16607
|
+
* @param options - 可选的数据源、打开面板通道与设置表单通道。
|
|
16608
|
+
*/
|
|
16609
|
+
function installProgressUpdatesClient(ctx, options = {}) {
|
|
16610
|
+
/**
|
|
16611
|
+
* 调用方显式给的数据源:给了就一直用它,不再发任何 HTTP(测试与将来替换用)。
|
|
16612
|
+
*/
|
|
16613
|
+
const explicitSource = options.source;
|
|
16614
|
+
/**
|
|
16615
|
+
* 默认数据源:每个会话一份轮询源。
|
|
16616
|
+
*
|
|
16617
|
+
* 槽位是 session 作用域的,同一个页签在会话切换时会被重新注入一次;按会话 id
|
|
16618
|
+
* 缓存,切回原会话不会白起一个客户端。sessionId 拿不到(`undefined`)时退化
|
|
16619
|
+
* 到不带 `?session=` 的请求,宿主对空 session 的语义是"当前/合并桶"。
|
|
16620
|
+
*/
|
|
16621
|
+
const hostSources = /* @__PURE__ */ new Map();
|
|
16622
|
+
/**
|
|
16623
|
+
* 取当前会话对应的一份宿主轮询源(不存在就建)。
|
|
16624
|
+
* @param sessionId - 框架注入的会话 id;`undefined` 表示当前没有会话身份。
|
|
16625
|
+
* @returns 该会话的面板数据源。
|
|
16626
|
+
*/
|
|
16627
|
+
const hostSourceOf = (sessionId) => {
|
|
16628
|
+
const key = sessionId ?? "";
|
|
16629
|
+
let source = hostSources.get(key);
|
|
16630
|
+
if (source === void 0) {
|
|
16631
|
+
source = createHostProgressSource(sessionId === void 0 || sessionId === "" ? void 0 : sessionId);
|
|
16632
|
+
hostSources.set(key, source);
|
|
16633
|
+
}
|
|
16634
|
+
return source;
|
|
16635
|
+
};
|
|
16636
|
+
/**
|
|
16637
|
+
* 设置表单通道。
|
|
16638
|
+
*
|
|
16639
|
+
* 调用方给了就用它;否则从 `ctx.configForms` 现取一份,并登记到本插件的 effect
|
|
16640
|
+
* 上(插件卸载时释放镜像订阅)。服务缺席时是 undefined——卡片据此渲染成只读。
|
|
16641
|
+
*/
|
|
16642
|
+
const settings = options.settings ?? (() => {
|
|
16643
|
+
const form = createProgressSettingsForm(ctx);
|
|
16644
|
+
if (form !== void 0) ctx.effect(() => () => {
|
|
16645
|
+
form.dispose();
|
|
16646
|
+
}, "dsh-progress-updates: settings form");
|
|
16647
|
+
return form;
|
|
16648
|
+
})();
|
|
16649
|
+
ctx.effect(() => () => {
|
|
16650
|
+
for (const source of hostSources.values()) try {
|
|
16651
|
+
source.stop();
|
|
16652
|
+
} catch {}
|
|
16653
|
+
hostSources.clear();
|
|
16654
|
+
}, "dsh-progress-updates: poll lifecycle");
|
|
16655
|
+
/**
|
|
16656
|
+
* 阶段一:声明页签**类型**。
|
|
16657
|
+
*
|
|
16658
|
+
* 这一步是面板能打开的前提。`patterns` 省略 = 页面型页签,按 kind 打开。
|
|
16659
|
+
* 服务不存在(没装 ui-sidebar-right)时整段跳过:对话流行与设置页照常挂上。
|
|
16660
|
+
*/
|
|
16661
|
+
ctx.effect(() => {
|
|
16662
|
+
const tabs = serviceOf(ctx, "sidebarRightTabs");
|
|
16663
|
+
if (typeof tabs?.register !== "function") return () => {};
|
|
16664
|
+
try {
|
|
16665
|
+
const definition = {
|
|
16666
|
+
id: PANEL_TAB_ID,
|
|
16667
|
+
kind: PANEL_TAB_KIND,
|
|
16668
|
+
title: () => {
|
|
16669
|
+
try {
|
|
16670
|
+
return ctx.locale.bind(NS_PROGRESS_UPDATES)("progressTitle");
|
|
16671
|
+
} catch {
|
|
16672
|
+
return zh.progressTitle;
|
|
16673
|
+
}
|
|
16674
|
+
},
|
|
16675
|
+
guide: [{
|
|
16676
|
+
id: "open",
|
|
16677
|
+
order: 60,
|
|
16678
|
+
title: () => {
|
|
16679
|
+
try {
|
|
16680
|
+
return ctx.locale.bind(NS_PROGRESS_UPDATES)("progressTitle");
|
|
16681
|
+
} catch {
|
|
16682
|
+
return zh.progressTitle;
|
|
16683
|
+
}
|
|
16684
|
+
},
|
|
16685
|
+
description: () => {
|
|
16686
|
+
try {
|
|
16687
|
+
return ctx.locale.bind(NS_PROGRESS_UPDATES)("progressGuideNote");
|
|
16688
|
+
} catch {
|
|
16689
|
+
return zh.progressGuideNote;
|
|
16690
|
+
}
|
|
16691
|
+
},
|
|
16692
|
+
icon: ProgressUpdatesArtwork
|
|
16693
|
+
}]
|
|
16694
|
+
};
|
|
16695
|
+
return tabs.register(definition);
|
|
16696
|
+
} catch {
|
|
16697
|
+
return () => {};
|
|
16698
|
+
}
|
|
16699
|
+
}, "dsh-progress-updates: tab type");
|
|
16700
|
+
/**
|
|
16701
|
+
* 对话流里的进展行:`progress_update` 每次调用渲染成一行。
|
|
16702
|
+
*
|
|
16703
|
+
* 这是本特写的主形态——不依赖侧栏,也不依赖宿主路由,只看工具调用的参数。
|
|
16704
|
+
*/
|
|
16705
|
+
ctx.slots.inject("tool.call.toolview", () => {
|
|
16706
|
+
try {
|
|
16707
|
+
return ctx.slots.register({
|
|
16708
|
+
name: "tool.call.toolview",
|
|
16709
|
+
key: PROGRESS_UPDATE_TOOL_NAME,
|
|
16710
|
+
locale: NS_PROGRESS_UPDATES
|
|
16711
|
+
}, ProgressRow);
|
|
16712
|
+
} catch {
|
|
16713
|
+
return () => {};
|
|
16714
|
+
}
|
|
16715
|
+
});
|
|
16716
|
+
/** 阶段二:同一个 id 下渲染页签正文。 */
|
|
16717
|
+
ctx.slots.inject("sidebar.right.pane.tab", () => {
|
|
16718
|
+
try {
|
|
16719
|
+
return ctx.slots.register({
|
|
16720
|
+
name: "sidebar.right.pane.tab",
|
|
16721
|
+
key: PANEL_TAB_ID,
|
|
16722
|
+
locale: NS_PROGRESS_UPDATES,
|
|
16723
|
+
inject: (sessionId) => ({ source: explicitSource ?? hostSourceOf(sessionId) })
|
|
16724
|
+
}, ProgressUpdatesTab);
|
|
16725
|
+
} catch {
|
|
16726
|
+
return () => {};
|
|
16727
|
+
}
|
|
16728
|
+
});
|
|
16729
|
+
/** 阶段二:同一个 id 下渲染页签条上的标题。 */
|
|
16730
|
+
ctx.slots.inject("sidebar.right.pane.tab.title", () => {
|
|
16731
|
+
try {
|
|
16732
|
+
return ctx.slots.register({
|
|
16733
|
+
name: "sidebar.right.pane.tab.title",
|
|
16734
|
+
key: PANEL_TAB_ID,
|
|
16735
|
+
locale: NS_PROGRESS_UPDATES
|
|
16736
|
+
}, ProgressUpdatesTabTitle);
|
|
16737
|
+
} catch {
|
|
16738
|
+
return () => {};
|
|
16739
|
+
}
|
|
16740
|
+
});
|
|
16741
|
+
/**
|
|
16742
|
+
* 设置页里的「进展更新」:**独立一格**,不与订阅服务那格合并。
|
|
16743
|
+
*
|
|
16744
|
+
* 没有这一格,面板就是注册好了也永远点不开——类型和正文都不会自己长出入口。
|
|
16745
|
+
*/
|
|
16746
|
+
ctx.slots.inject("settings.section", () => {
|
|
16747
|
+
try {
|
|
16748
|
+
const openPanel = openPanelAction(ctx, options.openPanel);
|
|
16749
|
+
return ctx.slots.register({
|
|
16750
|
+
name: "settings.section",
|
|
16751
|
+
id: SETTINGS_SECTION_ID,
|
|
16752
|
+
order: 150,
|
|
16753
|
+
label: () => {
|
|
16754
|
+
try {
|
|
16755
|
+
return ctx.locale.bind(NS_PROGRESS_UPDATES)("navTitle");
|
|
16756
|
+
} catch {
|
|
16757
|
+
return zh.navTitle;
|
|
16758
|
+
}
|
|
16759
|
+
},
|
|
16760
|
+
locale: NS_PROGRESS_UPDATES,
|
|
16761
|
+
inject: () => settings === void 0 ? { openPanel } : {
|
|
16762
|
+
openPanel,
|
|
16763
|
+
settings
|
|
16764
|
+
}
|
|
16765
|
+
}, ProgressUpdatesSettingsSection);
|
|
16766
|
+
} catch {
|
|
16767
|
+
return () => {};
|
|
16768
|
+
}
|
|
16769
|
+
});
|
|
16770
|
+
}
|
|
16771
|
+
//#endregion
|
|
16772
|
+
//#region src/client/progress-updates/styles.ts
|
|
16773
|
+
/**
|
|
16774
|
+
* 进展更新设置卡片的样式。
|
|
16775
|
+
*
|
|
16776
|
+
* ## 来源(方案 B:逐字抄进本插件,不做跨包同步机制)
|
|
16777
|
+
*
|
|
16778
|
+
* 结构与取值抄自同目录之外的订阅服务 hub 设置页(`../hub/hub-styles.ts` 的
|
|
16779
|
+
* `dsh-hub-card*` 系列):左侧方块 + 名称/说明两行 + 右侧控件,只有一条上边框、
|
|
16780
|
+
* 行与行靠各自的下边框连成整列。规则逐条照搬,只把类名前缀 `dsh-hub-` 换成
|
|
16781
|
+
* `dsh-progress-`,与那边互不覆盖。
|
|
16782
|
+
*
|
|
16783
|
+
* **枢纽改版了要人工同步**:两份文本没有构建期脚本或 CI 门禁比对。订阅服务调整
|
|
16784
|
+
* 这三行卡片的尺寸、间距、hover 或开关态时,这份文件不会自己跟上。
|
|
16785
|
+
*
|
|
16786
|
+
* ## 为什么是字符串而不是 CSS Modules
|
|
16787
|
+
*
|
|
16788
|
+
* 本插件其余线路(hub / antigravity / claude / kimi-code / step-code ……)全部走
|
|
16789
|
+
* `common/plugin-style.ts` 的 `installPluginStyle`:它把 CSS 作为一张带
|
|
16790
|
+
* `data-plugin` 归属的 <style> 注入,模块系统因此不会把这张表认领成别人的、
|
|
16791
|
+
* 也不会顺手删掉。CSS Modules 的 lightningcss 编译这条链路不在本插件的 tsdown
|
|
16792
|
+
* 配置里,所以这里与邻居保持一致,而不是把源实现的 `*.module.css` 搬过来。
|
|
16793
|
+
*
|
|
16794
|
+
* ## 配色
|
|
16795
|
+
*
|
|
16796
|
+
* 一律走 `--dsw-alias-*` 主题 token(每个都带明色兜底),暗色主题因此不需要
|
|
16797
|
+
* 单独一支分支。写死的只有开关滑块投影与品牌方块淡染这类"结构色"。
|
|
16798
|
+
*/
|
|
16799
|
+
/** 这张表的 `data-plugin-css` 子名。 */
|
|
16800
|
+
const STYLE_NAME = "progress-updates";
|
|
16801
|
+
/** 设置卡片与面板的样式全文。 */
|
|
16802
|
+
const CSS = `
|
|
16803
|
+
.dsh-progress-overview{display:flex;flex-direction:column;min-width:0}
|
|
16804
|
+
.dsh-progress-hint{color:var(--dsw-alias-label-secondary,#5d5c52);font-size:13px;line-height:1.55;margin:0 0 14px}
|
|
16805
|
+
.dsh-progress-cards{border-top:0.5px solid var(--dsw-alias-border-l2,#eceadf)}
|
|
16806
|
+
.dsh-progress-card{align-items:center;background:transparent;border:none;border-bottom:0.5px solid var(--dsw-alias-border-l2,#eceadf);color:var(--dsw-alias-label-primary,#141413);display:flex;gap:14px;font:inherit;min-height:68px;padding:12px 6px;text-align:left;width:100%}
|
|
16807
|
+
.dsh-progress-card[data-enabled=false] .dsh-progress-card-name,.dsh-progress-card[data-enabled=false] .dsh-progress-card-note{opacity:.55}
|
|
16808
|
+
.dsh-progress-card-copy{display:flex;flex:1;flex-direction:column;gap:3px;min-width:0}
|
|
16809
|
+
.dsh-progress-card-name{font-size:14px;font-weight:600;line-height:1.3}
|
|
16810
|
+
.dsh-progress-card-note{color:var(--dsw-alias-label-tertiary,#8f8d84);font-size:12px;line-height:1.4}
|
|
16811
|
+
.dsh-progress-card-note.dsh-progress-note-danger{color:var(--dsw-alias-label-danger,#d94b4b)}
|
|
16812
|
+
.dsh-progress-card-side{align-items:center;display:flex;flex:none;gap:10px}
|
|
16813
|
+
.dsh-progress-chevron{color:var(--dsw-alias-label-caption,#a6a094);display:inline-flex;flex:none}
|
|
16814
|
+
.dsh-progress-brand-tile{align-items:center;background:color-mix(in srgb,var(--dsh-progress-brand,#888) 10%,transparent);border-radius:10px;color:var(--dsh-progress-brand,#888);display:inline-flex;flex:none;justify-content:center;overflow:hidden}
|
|
16815
|
+
.dsh-progress-brand-tile>svg{display:block}
|
|
16816
|
+
.dsh-progress-switch{background:var(--dsw-alias-bg-layer-2,#f2f1ea);border:0.5px solid var(--dsw-alias-border-l2,#e3e1d5);border-radius:999px;box-sizing:border-box;cursor:pointer;flex:none;height:24px;padding:2px;position:relative;transition:background .15s ease,border-color .15s ease;width:44px}
|
|
16817
|
+
.dsh-progress-switch::after{background:var(--dsw-alias-switch-thumb,var(--dsw-alias-bg-layer-1,#fff));border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,.28);content:"";display:block;height:19px;transition:transform .15s ease;width:19px}
|
|
16818
|
+
.dsh-progress-switch[aria-checked=true]{background:var(--dsw-alias-button-info-fill,#397ee8);border-color:transparent}
|
|
16819
|
+
.dsh-progress-switch[aria-checked=true]::after{transform:translateX(20px)}
|
|
16820
|
+
.dsh-progress-switch:disabled{cursor:default;opacity:.5}
|
|
16821
|
+
.dsh-progress-switch:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
|
|
16822
|
+
.dsh-progress-number{background:var(--dsw-alias-bg-layer-1,#fff);border:0.5px solid var(--dsw-alias-border-l2,#e3e1d5);border-radius:8px;box-sizing:border-box;color:var(--dsw-alias-label-primary,#141413);font:inherit;font-size:13px;height:30px;padding:0 8px;text-align:center;width:64px}
|
|
16823
|
+
.dsh-progress-number:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:1px}
|
|
16824
|
+
.dsh-progress-number:disabled{cursor:default;opacity:.5}
|
|
16825
|
+
.dsh-progress-open{align-items:center;background:transparent;border:0.5px solid var(--dsw-alias-border-l2,#e3e1d5);border-radius:8px;color:var(--dsw-alias-label-primary,#141413);cursor:pointer;display:inline-flex;font:inherit;font-size:12px;gap:2px;padding:5px 8px}
|
|
16826
|
+
.dsh-progress-open:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.04))}
|
|
16827
|
+
.dsh-progress-open:focus-visible{outline:2px solid var(--dsw-alias-button-info-fill,#397ee8);outline-offset:2px}
|
|
16828
|
+
`;
|
|
16829
|
+
/**
|
|
16830
|
+
* 安装「进展更新」设置卡片的样式表(幂等;标签归文档所有,卸载时不移除)。
|
|
16831
|
+
* @returns 空操作释放函数(与邻居同形)。
|
|
16832
|
+
*/
|
|
16833
|
+
function installProgressUpdatesStyles() {
|
|
16834
|
+
return installPluginStyle(STYLE_NAME, CSS);
|
|
16835
|
+
}
|
|
16836
|
+
//#endregion
|
|
14783
16837
|
//#region src/client/index.tsx
|
|
14784
16838
|
const inject = [
|
|
14785
16839
|
"slots",
|
|
@@ -14791,46 +16845,51 @@ window.__ModuleLoader__.load({
|
|
|
14791
16845
|
"remote.session"
|
|
14792
16846
|
];
|
|
14793
16847
|
function apply(ctx) {
|
|
14794
|
-
ctx.effect(() => ctx.locale.register(NS, dictionaries), "dsh-chatgpt-subscription: dictionaries");
|
|
16848
|
+
ctx.effect(() => ctx.locale.register(NS, dictionaries$1), "dsh-chatgpt-subscription: dictionaries");
|
|
14795
16849
|
ctx.effect(() => installStyles(), "dsh-chatgpt-subscription: styles");
|
|
14796
16850
|
ctx.effect(() => {
|
|
14797
16851
|
installHubStyles();
|
|
14798
16852
|
installModelChecklistStyles();
|
|
14799
16853
|
return () => {};
|
|
14800
16854
|
}, "dsh-chatgpt-subscription: hub styles");
|
|
14801
|
-
ctx.effect(() => ctx.locale.register(NS_ANTIGRAVITY, dictionaries$
|
|
16855
|
+
ctx.effect(() => ctx.locale.register(NS_ANTIGRAVITY, dictionaries$10), "dsh-antigravity: dictionaries");
|
|
14802
16856
|
ctx.effect(() => {
|
|
14803
16857
|
installAntigravityStyles();
|
|
14804
16858
|
return () => {};
|
|
14805
16859
|
}, "dsh-antigravity: styles");
|
|
14806
|
-
ctx.effect(() => ctx.locale.register(NS_CLAUDE, dictionaries$
|
|
16860
|
+
ctx.effect(() => ctx.locale.register(NS_CLAUDE, dictionaries$4), "dsh-claude: dictionaries");
|
|
14807
16861
|
ctx.effect(() => {
|
|
14808
16862
|
installClaudeStyles();
|
|
14809
16863
|
return () => {};
|
|
14810
16864
|
}, "dsh-claude: styles");
|
|
14811
|
-
ctx.effect(() => ctx.locale.register(NS_COMMAND_CODE, dictionaries$
|
|
14812
|
-
ctx.effect(() => ctx.locale.register(NS_KIMI_CODE, dictionaries$
|
|
16865
|
+
ctx.effect(() => ctx.locale.register(NS_COMMAND_CODE, dictionaries$9), "dsh-command-code: dictionaries");
|
|
16866
|
+
ctx.effect(() => ctx.locale.register(NS_KIMI_CODE, dictionaries$8), "dsh-kimi-code: dictionaries");
|
|
14813
16867
|
ctx.effect(() => {
|
|
14814
16868
|
installKimiCodeStyles();
|
|
14815
16869
|
return () => {};
|
|
14816
16870
|
}, "dsh-kimi-code: styles");
|
|
14817
|
-
ctx.effect(() => ctx.locale.register(NS_MINIMAX_CODE, dictionaries$
|
|
16871
|
+
ctx.effect(() => ctx.locale.register(NS_MINIMAX_CODE, dictionaries$6), "dsh-minimax-code: dictionaries");
|
|
14818
16872
|
ctx.effect(() => {
|
|
14819
16873
|
installMinimaxCodeStyles();
|
|
14820
16874
|
return () => {};
|
|
14821
16875
|
}, "dsh-minimax-code: styles");
|
|
14822
|
-
ctx.effect(() => ctx.locale.register(NS_WORKBUDDY, dictionaries$
|
|
14823
|
-
ctx.effect(() => ctx.locale.register(NS_GLM_CODING_PLAN, dictionaries$
|
|
16876
|
+
ctx.effect(() => ctx.locale.register(NS_WORKBUDDY, dictionaries$7), "dsh-workbuddy: dictionaries");
|
|
16877
|
+
ctx.effect(() => ctx.locale.register(NS_GLM_CODING_PLAN, dictionaries$5), "dsh-glm-coding-plan: dictionaries");
|
|
14824
16878
|
ctx.effect(() => {
|
|
14825
16879
|
installGlmCodingPlanStyles();
|
|
14826
16880
|
return () => {};
|
|
14827
16881
|
}, "dsh-glm-coding-plan: styles");
|
|
14828
|
-
ctx.effect(() => ctx.locale.register(NS_OLLAMA, dictionaries$
|
|
16882
|
+
ctx.effect(() => ctx.locale.register(NS_OLLAMA, dictionaries$3), "dsh-ollama: dictionaries");
|
|
14829
16883
|
ctx.effect(() => {
|
|
14830
16884
|
installWorkBuddyStyles();
|
|
14831
16885
|
return () => {};
|
|
14832
16886
|
}, "dsh-workbuddy: styles");
|
|
14833
|
-
ctx.effect(() => ctx.locale.register(
|
|
16887
|
+
ctx.effect(() => ctx.locale.register(NS_PROGRESS_UPDATES, dictionaries), "dsh-progress-updates: dictionaries");
|
|
16888
|
+
ctx.effect(() => {
|
|
16889
|
+
installProgressUpdatesStyles();
|
|
16890
|
+
return () => {};
|
|
16891
|
+
}, "dsh-progress-updates: styles");
|
|
16892
|
+
ctx.effect(() => ctx.locale.register(NS_STEP_CODE, dictionaries$2), "dsh-step-code: dictionaries");
|
|
14834
16893
|
ctx.effect(() => {
|
|
14835
16894
|
installStepCodeStyles();
|
|
14836
16895
|
return () => {};
|
|
@@ -14936,6 +16995,7 @@ window.__ModuleLoader__.load({
|
|
|
14936
16995
|
locale: NS,
|
|
14937
16996
|
inject: (sessionId) => ({ loadImage: imageLoader(ctx, sessionId) })
|
|
14938
16997
|
}, CodexImageToolView));
|
|
16998
|
+
installProgressUpdatesClient(ctx);
|
|
14939
16999
|
}
|
|
14940
17000
|
/**
|
|
14941
17001
|
* The seats every composer badge is handed.
|