@jingyi0605/codingns4dsh 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +306 -0
- package/README.md +190 -993
- package/data/build/dist/client/account-bar.d.ts +2 -2
- package/data/build/dist/client/account-bar.js +96 -39
- package/data/build/dist/client/bundle.js +13722 -3611
- package/data/build/dist/client/cli-catalog.js +20 -3
- package/data/build/dist/client/cli-slots.d.ts +5 -0
- package/data/build/dist/client/cli-slots.js +57 -7
- package/data/build/dist/client/debug/ui.d.ts +1 -1
- package/data/build/dist/client/debug/ui.js +62 -32
- package/data/build/dist/client/dsh-runtime-version.d.ts +9 -2
- package/data/build/dist/client/dsh-runtime-version.js +24 -2
- package/data/build/dist/client/features/cli-adapters.d.ts +1 -1
- package/data/build/dist/client/features/cli-adapters.js +19 -16
- package/data/build/dist/client/features/debug.js +2 -2
- package/data/build/dist/client/features/index.d.ts +2 -1
- package/data/build/dist/client/features/index.js +3 -1
- package/data/build/dist/client/features/lan-access-panel.d.ts +1 -1
- package/data/build/dist/client/features/lan-access-panel.js +8 -10
- package/data/build/dist/client/features/lan-access.js +1 -1
- package/data/build/dist/client/features/login-protection-panel.d.ts +1 -1
- package/data/build/dist/client/features/login-protection-panel.js +7 -9
- package/data/build/dist/client/features/reverse-proxy-panel.d.ts +1 -1
- package/data/build/dist/client/features/reverse-proxy-panel.js +99 -11
- package/data/build/dist/client/features/reverse-proxy.d.ts +1 -1
- package/data/build/dist/client/features/reverse-proxy.js +19 -18
- package/data/build/dist/client/features/terminal-enhancement-panel.d.ts +1 -1
- package/data/build/dist/client/features/terminal-enhancement-panel.js +62 -55
- package/data/build/dist/client/features/terminal-enhancement.d.ts +1 -1
- package/data/build/dist/client/features/terminal-enhancement.js +3 -3
- package/data/build/dist/client/features/types.d.ts +14 -3
- package/data/build/dist/client/features/workspace-session-enhancement-panel.d.ts +1 -1
- package/data/build/dist/client/features/workspace-session-enhancement-panel.js +31 -9
- package/data/build/dist/client/features/workspace-session-enhancement.js +72 -11
- package/data/build/dist/client/git-management.d.ts +16 -0
- package/data/build/dist/client/git-management.js +757 -0
- package/data/build/dist/client/index.d.ts +3 -2
- package/data/build/dist/client/index.js +152 -27
- package/data/build/dist/client/locale.js +91 -29
- package/data/build/dist/client/quick-phrase-layout.d.ts +49 -0
- package/data/build/dist/client/quick-phrase-layout.js +95 -0
- package/data/build/dist/client/quick-phrase-slot.d.ts +7 -0
- package/data/build/dist/client/quick-phrase-slot.js +370 -0
- package/data/build/dist/client/remote-web-context.js +1 -1
- package/data/build/dist/client/settings-bridge.d.ts +9 -6
- package/data/build/dist/client/settings-bridge.js +51 -27
- package/data/build/dist/client/settings-section.d.ts +3 -2
- package/data/build/dist/client/settings-section.js +33 -19
- package/data/build/dist/client/subscription-slot.d.ts +3 -0
- package/data/build/dist/client/subscription-slot.js +108 -16
- package/data/build/dist/client/terminal/model.d.ts +14 -2
- package/data/build/dist/client/terminal/model.js +109 -21
- package/data/build/dist/client/terminal/recovery.d.ts +50 -0
- package/data/build/dist/client/terminal/recovery.js +101 -0
- package/data/build/dist/client/terminal/ui.d.ts +2 -2
- package/data/build/dist/client/terminal/ui.js +49 -11
- package/data/build/dist/client/terminal/xterm-view.d.ts +2 -2
- package/data/build/dist/client/terminal/xterm-view.js +2 -1
- package/data/build/dist/client/theme.d.ts +6 -1
- package/data/build/dist/client/theme.js +32 -1
- package/data/build/dist/client/workspace-session-archive-dom.d.ts +12 -0
- package/data/build/dist/client/workspace-session-archive-dom.js +27 -5
- package/data/build/dist/client/workspace-session-visibility-dom.d.ts +30 -0
- package/data/build/dist/client/workspace-session-visibility-dom.js +604 -0
- package/data/build/dist/dsh-capabilities/client/config-forms-adapter.d.ts +50 -0
- package/data/build/dist/dsh-capabilities/client/config-forms-adapter.js +139 -0
- package/data/build/dist/dsh-capabilities/client/conversation-adapter.d.ts +7 -0
- package/data/build/dist/dsh-capabilities/client/conversation-adapter.js +6 -0
- package/data/build/dist/dsh-capabilities/client/locale-adapter.d.ts +9 -0
- package/data/build/dist/dsh-capabilities/client/locale-adapter.js +4 -0
- package/data/build/dist/dsh-capabilities/client/primitives-adapter.d.ts +10 -0
- package/data/build/dist/dsh-capabilities/client/primitives-adapter.js +14 -0
- package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.d.ts +8 -0
- package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.js +16 -0
- package/data/build/dist/dsh-capabilities/client/sidebar-adapter.d.ts +7 -0
- package/data/build/dist/dsh-capabilities/client/sidebar-adapter.js +5 -0
- package/data/build/dist/dsh-capabilities/client/theme-adapter.d.ts +6 -0
- package/data/build/dist/dsh-capabilities/client/theme-adapter.js +4 -0
- package/data/build/dist/dsh-capabilities/client/typert-adapter.d.ts +5 -0
- package/data/build/dist/dsh-capabilities/client/typert-adapter.js +4 -0
- package/data/build/dist/dsh-capabilities/host/config-forms-adapter.d.ts +19 -0
- package/data/build/dist/dsh-capabilities/host/config-forms-adapter.js +45 -0
- package/data/build/dist/dsh-capabilities/host/connection-rpc-adapter.d.ts +19 -0
- package/data/build/dist/dsh-capabilities/host/connection-rpc-adapter.js +13 -0
- package/data/build/dist/dsh-capabilities/host/settings-scope-adapter.d.ts +8 -0
- package/data/build/dist/dsh-capabilities/host/settings-scope-adapter.js +47 -0
- package/data/build/dist/dsh-capabilities/host/typert-adapter.d.ts +6 -0
- package/data/build/dist/dsh-capabilities/host/typert-adapter.js +4 -0
- package/data/build/dist/dsh-capabilities/index.d.ts +6 -0
- package/data/build/dist/dsh-capabilities/index.js +6 -0
- package/data/build/dist/dsh-capabilities/matrix.d.ts +15 -0
- package/data/build/dist/dsh-capabilities/matrix.js +32 -0
- package/data/build/dist/dsh-capabilities/registry.d.ts +22 -0
- package/data/build/dist/dsh-capabilities/registry.js +196 -0
- package/data/build/dist/dsh-capabilities/routes.d.ts +10 -0
- package/data/build/dist/dsh-capabilities/routes.js +34 -0
- package/data/build/dist/dsh-capabilities/settings-store.d.ts +29 -0
- package/data/build/dist/dsh-capabilities/settings-store.js +5 -0
- package/data/build/dist/dsh-capabilities/types.d.ts +50 -0
- package/data/build/dist/dsh-capabilities/types.js +23 -0
- package/data/build/dist/features/registry.d.ts +8 -2
- package/data/build/dist/features/registry.js +104 -1
- package/data/build/dist/host/auth-session.d.ts +8 -1
- package/data/build/dist/host/auth-session.js +28 -6
- package/data/build/dist/host/cli-adapters/claude-driver.d.ts +2 -0
- package/data/build/dist/host/cli-adapters/claude-driver.js +19 -4
- package/data/build/dist/host/cli-adapters/claude-model-options.d.ts +14 -0
- package/data/build/dist/host/cli-adapters/claude-model-options.js +157 -0
- package/data/build/dist/host/cli-adapters/codex-driver.d.ts +11 -0
- package/data/build/dist/host/cli-adapters/codex-driver.js +166 -14
- package/data/build/dist/host/cli-adapters/driver.d.ts +2 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +15 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.js +120 -29
- package/data/build/dist/host/cli-adapters/dsh-tool-history.d.ts +4 -1
- package/data/build/dist/host/cli-adapters/dsh-tool-history.js +25 -7
- package/data/build/dist/host/cli-adapters/feature.js +74 -8
- package/data/build/dist/host/cli-adapters/gemini-driver.js +2 -2
- package/data/build/dist/host/cli-adapters/grok-driver.js +2 -2
- package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +4 -2
- package/data/build/dist/host/cli-adapters/json-rpc-process.js +8 -9
- package/data/build/dist/host/cli-adapters/kimi-driver.js +1 -1
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts +17 -0
- package/data/build/dist/host/cli-adapters/legacy-session-adapter.js +88 -0
- package/data/build/dist/host/cli-adapters/legacy-session-settings.d.ts +24 -0
- package/data/build/dist/host/cli-adapters/legacy-session-settings.js +162 -0
- package/data/build/dist/host/cli-adapters/official-provider-subscription.d.ts +29 -0
- package/data/build/dist/host/cli-adapters/official-provider-subscription.js +361 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +2 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.js +97 -12
- package/data/build/dist/host/cli-adapters/pi-driver.js +69 -23
- package/data/build/dist/host/cli-adapters/provider-registry.d.ts +33 -0
- package/data/build/dist/host/cli-adapters/provider-registry.js +83 -0
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +25 -3
- package/data/build/dist/host/cli-adapters/provider-subscription.js +346 -30
- package/data/build/dist/host/cli-adapters/registry.d.ts +6 -0
- package/data/build/dist/host/cli-adapters/registry.js +170 -13
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +10 -0
- package/data/build/dist/host/cli-adapters/session-store.d.ts +7 -0
- package/data/build/dist/host/cli-adapters/session-store.js +39 -1
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -0
- package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts +3 -0
- package/data/build/dist/host/cli-adapters/stream-normalizer.js +22 -2
- package/data/build/dist/host/control-api-client.d.ts +3 -3
- package/data/build/dist/host/control-api-client.js +2 -1
- package/data/build/dist/host/credential-store.js +11 -1
- package/data/build/dist/host/dsh-device-runtime.d.ts +4 -0
- package/data/build/dist/host/dsh-device-runtime.js +20 -9
- package/data/build/dist/host/features/auth.js +8 -5
- package/data/build/dist/host/features/debug.js +1 -1
- package/data/build/dist/host/features/git-management.d.ts +5 -0
- package/data/build/dist/host/features/git-management.js +290 -0
- package/data/build/dist/host/features/host-status.js +1 -1
- package/data/build/dist/host/features/index.d.ts +1 -0
- package/data/build/dist/host/features/index.js +3 -0
- package/data/build/dist/host/features/terminal-process.js +1 -1
- package/data/build/dist/host/features/terminal-status.d.ts +3 -1
- package/data/build/dist/host/features/terminal-status.js +3 -1
- package/data/build/dist/host/index.d.ts +7 -3
- package/data/build/dist/host/index.js +76 -11
- package/data/build/dist/host/lan-access-dsh.js +18 -5
- package/data/build/dist/host/native-session-bridge.d.ts +23 -1
- package/data/build/dist/host/native-session-bridge.js +157 -10
- package/data/build/dist/host/rpc-table.d.ts +1 -1
- package/data/build/dist/host/rpc.js +60 -10
- package/data/build/dist/host/session-migration-repair.d.ts +19 -0
- package/data/build/dist/host/session-migration-repair.js +395 -0
- package/data/build/dist/host/settings.d.ts +8 -1
- package/data/build/dist/host/settings.js +94 -3
- package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +1 -0
- package/data/build/dist/host/terminal/backends/tmux-backend.js +4 -2
- package/data/build/dist/host/terminal/controller-factory.d.ts +4 -0
- package/data/build/dist/host/terminal/controller-factory.js +26 -7
- package/data/build/dist/host/terminal/runtime-manager.d.ts +2 -1
- package/data/build/dist/host/terminal/runtime-manager.js +3 -0
- package/data/build/dist/host/terminal/startup.d.ts +2 -0
- package/data/build/dist/host/terminal/startup.js +2 -1
- package/data/build/dist/host/terminal/terminal-controller.d.ts +10 -3
- package/data/build/dist/host/terminal/terminal-controller.js +101 -15
- package/data/build/dist/host/terminal/terminal-process-service.js +3 -1
- package/data/build/dist/host/terminal/terminal-service.d.ts +2 -0
- package/data/build/dist/host/terminal/terminal-service.js +10 -0
- package/data/build/dist/index.d.ts +6 -2
- package/data/build/dist/index.js +8 -2
- package/data/build/dist/shared/contracts/cli-adapter.d.ts +14 -0
- package/data/build/dist/shared/contracts/config.d.ts +31 -1
- package/data/build/dist/shared/contracts/config.js +25 -0
- package/data/build/dist/shared/contracts/dsh-device.d.ts +13 -1
- package/data/build/dist/shared/contracts/errors.d.ts +1 -1
- package/data/build/dist/shared/contracts/feature.d.ts +6 -0
- package/data/build/dist/shared/contracts/git.d.ts +82 -0
- package/data/build/dist/shared/contracts/git.js +3 -0
- package/data/build/dist/shared/contracts/subscription.d.ts +46 -0
- package/data/build/dist/shared/contracts/terminal.d.ts +4 -1
- package/data/build/dist/shared/contracts/version.d.ts +2 -2
- package/data/build/dist/shared/contracts/version.js +4 -6
- package/data/build/dist/shared/debug.d.ts +14 -0
- package/data/build/dist/shared/debug.js +53 -0
- package/data/build/dist/shared/index.d.ts +7 -4
- package/data/build/dist/shared/index.js +2 -1
- package/data/build/dist/transport/debug.d.ts +1 -1
- package/data/build/dist/transport/debug.js +3 -30
- package/data/build/dist/transport/dsh-session.js +3 -3
- package/data/build/dist/typert.host.js +4 -2
- package/data/build/dist/typert.remote-client.d.ts +11 -0
- package/data/build/dist/typert.remote-client.js +7 -0
- package/dsh.bundle.patch +1 -1
- package/package.json +17 -5
- package/profile/README.md +4 -4
- package/profile/package.json +6 -4
- package/profile/scripts/check-dsh-install.mjs +2 -2
- package/profile/version.json +2 -2
- package/scripts/check-dsh-install.mjs +2 -2
- package/version.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CODINGNS_RPC_CHANNEL } from '../shared/contracts/transport.js';
|
|
2
|
+
import { debugInfo, debugWarn } from '../shared/debug.js';
|
|
2
3
|
/**
|
|
3
4
|
* 把 DSH 本地设置和远程 Host 设置 RPC 统一成一个设置作用域。
|
|
4
5
|
* 非回环页面使用 RPC,回环页面完全复用 DSH 的原生设置传输。
|
|
@@ -14,14 +15,14 @@ export class CodingNsSettingsBridge {
|
|
|
14
15
|
constructor(local, rpc) {
|
|
15
16
|
this.local = local;
|
|
16
17
|
this.rpc = rpc;
|
|
17
|
-
this.snapshot = local.getSnapshot();
|
|
18
|
+
this.snapshot = toStoreSnapshot(local.getSnapshot());
|
|
18
19
|
this.localUnsubscribe = local.subscribe(() => {
|
|
19
20
|
if (this.isRemote()) {
|
|
20
21
|
void this.load().catch(() => undefined);
|
|
21
22
|
return;
|
|
22
23
|
}
|
|
23
24
|
this.remoteLoaded = false;
|
|
24
|
-
this.publish(local.getSnapshot());
|
|
25
|
+
this.publish(toStoreSnapshot(local.getSnapshot()));
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
28
|
getSnapshot() { return this.snapshot; }
|
|
@@ -36,35 +37,40 @@ export class CodingNsSettingsBridge {
|
|
|
36
37
|
return;
|
|
37
38
|
if (this.remoteLoad !== undefined)
|
|
38
39
|
return this.remoteLoad;
|
|
40
|
+
debugInfo('codingns4dsh: client settings load begin');
|
|
39
41
|
this.remoteLoad = this.call('settings/get', {}).then((response) => {
|
|
40
42
|
this.remoteLoaded = true;
|
|
41
43
|
this.publish({
|
|
42
44
|
status: 'ready',
|
|
43
45
|
value: response.value,
|
|
44
|
-
base: undefined,
|
|
45
|
-
user: undefined,
|
|
46
46
|
revision: response.revision,
|
|
47
47
|
writable: true,
|
|
48
|
-
mode: 'host',
|
|
49
48
|
});
|
|
49
|
+
debugInfo('codingns4dsh: client settings load success', { revision: response.revision });
|
|
50
50
|
}).finally(() => {
|
|
51
51
|
this.remoteLoad = undefined;
|
|
52
52
|
});
|
|
53
53
|
return this.remoteLoad;
|
|
54
54
|
}
|
|
55
55
|
async set(field, value) {
|
|
56
|
-
if (!this.isRemote())
|
|
57
|
-
|
|
56
|
+
if (!this.isRemote()) {
|
|
57
|
+
await this.local.set(field, value);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
58
60
|
return this.mutate([{ op: 'set', path: [field], value: toJsonValue(value) }]);
|
|
59
61
|
}
|
|
60
62
|
async unset(field) {
|
|
61
|
-
if (!this.isRemote())
|
|
62
|
-
|
|
63
|
+
if (!this.isRemote()) {
|
|
64
|
+
await this.local.unset(field);
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
63
67
|
return this.mutate([{ op: 'unset', path: [field] }]);
|
|
64
68
|
}
|
|
65
69
|
async mutate(ops, expectedRevision) {
|
|
66
|
-
if (!this.isRemote())
|
|
67
|
-
|
|
70
|
+
if (!this.isRemote()) {
|
|
71
|
+
await this.local.mutate(ops, expectedRevision);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
68
74
|
const payload = expectedRevision === undefined ? { ops } : { ops, expectedRevision };
|
|
69
75
|
const response = await this.call('settings/set', payload);
|
|
70
76
|
this.remoteLoaded = true;
|
|
@@ -74,26 +80,12 @@ export class CodingNsSettingsBridge {
|
|
|
74
80
|
value: response.value,
|
|
75
81
|
revision: response.revision,
|
|
76
82
|
writable: true,
|
|
77
|
-
mode: 'host',
|
|
78
83
|
});
|
|
84
|
+
return true;
|
|
79
85
|
}
|
|
80
86
|
dispose() { this.localUnsubscribe(); }
|
|
81
87
|
async call(endpoint, payload) {
|
|
82
|
-
|
|
83
|
-
try {
|
|
84
|
-
result = await this.rpc.call(CODINGNS_RPC_CHANNEL, endpoint, payload);
|
|
85
|
-
}
|
|
86
|
-
catch (error) {
|
|
87
|
-
// DSH 原生连接通常把自定义 RPC 映射到 /api;保留逻辑通道兼容
|
|
88
|
-
// Codingns4DSH Transport,同时在普通 Web Host 上回退到实际 Fetch 路由。
|
|
89
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
90
|
-
if (!/HTTP (?:404|405)\b/u.test(message))
|
|
91
|
-
throw error;
|
|
92
|
-
result = await this.rpc.call('/api', `codingns/${endpoint}`, payload);
|
|
93
|
-
}
|
|
94
|
-
if (!result.ok)
|
|
95
|
-
throw new Error(result.error.message);
|
|
96
|
-
return result.value;
|
|
88
|
+
return callCodingNsRpc(this.rpc, endpoint, payload);
|
|
97
89
|
}
|
|
98
90
|
publish(next) {
|
|
99
91
|
this.snapshot = next;
|
|
@@ -105,6 +97,35 @@ export class CodingNsSettingsBridge {
|
|
|
105
97
|
return snapshot.mode === 'memory' || snapshot.status === 'unavailable';
|
|
106
98
|
}
|
|
107
99
|
}
|
|
100
|
+
/** 调用 Codingns4DSH Host RPC,并兼容 DSH 原生连接与普通 Web API 回退。 */
|
|
101
|
+
export async function callCodingNsRpc(rpc, endpoint, payload) {
|
|
102
|
+
let result;
|
|
103
|
+
try {
|
|
104
|
+
debugInfo('codingns4dsh: client rpc request', { channel: CODINGNS_RPC_CHANNEL, endpoint });
|
|
105
|
+
result = await rpc.call(CODINGNS_RPC_CHANNEL, endpoint, payload);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
// DSH 原生连接通常把自定义 RPC 映射到 /api;保留逻辑通道兼容
|
|
109
|
+
// Codingns4DSH Transport,同时在普通 Web Host 上回退到实际 Fetch 路由。
|
|
110
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
111
|
+
if (!/HTTP (?:404|405)\b/u.test(message))
|
|
112
|
+
throw error;
|
|
113
|
+
debugWarn('codingns4dsh: client rpc fallback', { endpoint, error: message });
|
|
114
|
+
try {
|
|
115
|
+
result = await rpc.call('/api', `codingns/${endpoint}`, payload);
|
|
116
|
+
}
|
|
117
|
+
catch (fallbackError) {
|
|
118
|
+
console.error('codingns4dsh: client rpc fallback failed', { endpoint, error: fallbackError });
|
|
119
|
+
throw fallbackError;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (!result.ok) {
|
|
123
|
+
console.error('codingns4dsh: client rpc response error', { endpoint, error: result.error });
|
|
124
|
+
throw new Error(result.error.message);
|
|
125
|
+
}
|
|
126
|
+
debugInfo('codingns4dsh: client rpc response success', { endpoint });
|
|
127
|
+
return result.value;
|
|
128
|
+
}
|
|
108
129
|
/** 设置 RPC 只能传 JSON;在浏览器边界尽早拒绝函数、循环引用等无效值。 */
|
|
109
130
|
function toJsonValue(value) {
|
|
110
131
|
if (value === null || typeof value === 'string' || typeof value === 'boolean')
|
|
@@ -123,4 +144,7 @@ function toJsonValue(value) {
|
|
|
123
144
|
export function createCodingNsSettingsBridge(local, rpc) {
|
|
124
145
|
return new CodingNsSettingsBridge(local, rpc);
|
|
125
146
|
}
|
|
147
|
+
function toStoreSnapshot(snapshot) {
|
|
148
|
+
return { value: snapshot.value, revision: snapshot.revision, writable: snapshot.writable, status: snapshot.status };
|
|
149
|
+
}
|
|
126
150
|
//# sourceMappingURL=settings-bridge.js.map
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { ReactElement } from 'react';
|
|
2
2
|
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
3
|
-
import type {
|
|
3
|
+
import type { SettingsSectionOwnerProps } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
4
4
|
import type { FeatureRegistry } from '../features/registry.js';
|
|
5
5
|
import { type RestartFeatureStates, type CodingNsSettings } from '../shared/contracts/config.js';
|
|
6
6
|
import type { CodingNsClientFeatureModule, CodingNsClientServices } from './features/types.js';
|
|
7
|
+
import type { CodingNsSettingsStore } from '../dsh-capabilities/settings-store.js';
|
|
7
8
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
8
9
|
interface SlotMap {
|
|
9
10
|
'settings.section': {
|
|
@@ -14,7 +15,7 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
export interface CodingNsSectionProps extends PropsRuntime<'settings.section'> {
|
|
17
|
-
readonly settings:
|
|
18
|
+
readonly settings: CodingNsSettingsStore<CodingNsSettings>;
|
|
18
19
|
readonly registry: FeatureRegistry<CodingNsClientServices, CodingNsClientFeatureModule>;
|
|
19
20
|
readonly services: CodingNsClientServices;
|
|
20
21
|
/** 当前 Client 进程启动时捕获的重启生效模块状态。 */
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { createElement, useState, useSyncExternalStore } from 'react';
|
|
1
|
+
import { createElement, useEffect, useState, useSyncExternalStore } from 'react';
|
|
2
2
|
import { CODINGNS_MODULES_FIELD, isFeatureEnabled, isFeatureDshVersionCompatible, } from '../shared/contracts/config.js';
|
|
3
3
|
import { settingsModules } from './features/index.js';
|
|
4
|
-
import { dshSettingsBodyStyle, dshSettingsCardStyle, dshSettingsHeaderStyle, dshSettingsPageStyle, dshSettingsSubtitleStyle, dshSettingsSummaryDescriptionStyle, dshSettingsSummaryLabelStyle, dshSettingsSummaryStyle, dshSettingsSummaryTextStyle, dshSettingsTitleStyle, dshThemeColor, } from './theme.js';
|
|
4
|
+
import { dshSettingsBodyStyle, dshSettingsCardStyle, dshSettingsHeaderStyle, dshSettingsPageStyle, dshSettingsSubtitleStyle, dshSettingsSummaryDescriptionStyle, dshSettingsSummaryLabelStyle, dshSettingsSummaryStyle, dshSettingsSummaryTextStyle, dshSettingsTitleStyle, dshSettingsToastStyle, dshThemeColor, } from './theme.js';
|
|
5
5
|
import { useCodingNsTranslator } from './locale.js';
|
|
6
|
-
import { isLegacyDshVersion } from '../shared/contracts/version.js';
|
|
6
|
+
import { CODINGNS_VERSION, DSH_COMPATIBILITY, isLegacyDshVersion } from '../shared/contracts/version.js';
|
|
7
|
+
const CODINGNS_GITHUB_URL = 'https://github.com/jingyi0605/Codingns4DSH';
|
|
7
8
|
/**
|
|
8
9
|
* DSH 设置页中的 Codingns4DSH 区块。
|
|
9
10
|
*
|
|
@@ -13,41 +14,56 @@ import { isLegacyDshVersion } from '../shared/contracts/version.js';
|
|
|
13
14
|
export function CodingNsSettingsSection({ settings, registry, services, restartStates = {} }) {
|
|
14
15
|
const snapshot = useSyncExternalStore((listener) => settings.subscribe(listener), () => settings.getSnapshot(), () => settings.getSnapshot());
|
|
15
16
|
const t = useCodingNsTranslator(services.locale);
|
|
16
|
-
|
|
17
|
+
const [toast, setToast] = useState(null);
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (toast === null)
|
|
20
|
+
return;
|
|
21
|
+
const timer = globalThis.setTimeout(() => setToast(null), 3200);
|
|
22
|
+
return () => globalThis.clearTimeout(timer);
|
|
23
|
+
}, [toast]);
|
|
24
|
+
const notify = (notice) => setToast(notice);
|
|
25
|
+
return createElement('section', { style: dshSettingsPageStyle }, toast === null ? null : createElement('div', {
|
|
26
|
+
role: toast.kind === 'error' ? 'alert' : 'status',
|
|
27
|
+
'aria-live': 'polite',
|
|
28
|
+
style: { ...dshSettingsToastStyle, borderColor: toast.kind === 'error' ? dshThemeColor.error : toast.kind === 'success' ? dshThemeColor.success : dshThemeColor.border },
|
|
29
|
+
}, toast.message), createElement('header', { style: dshSettingsHeaderStyle }, createElement('h2', { style: dshSettingsTitleStyle }, t('settings.title')), createElement('p', { style: dshSettingsSubtitleStyle }, t('settings.subtitle'))), createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 12 } }, settingsModules(registry).map((entry) => createElement(FeatureCard, {
|
|
17
30
|
key: entry.module.descriptor.name,
|
|
18
31
|
entry,
|
|
19
32
|
snapshot,
|
|
20
33
|
services,
|
|
21
34
|
restartStates,
|
|
22
|
-
|
|
35
|
+
notify,
|
|
36
|
+
}))), createElement('details', { style: { alignSelf: 'center', display: 'flex', flexDirection: 'column-reverse', alignItems: 'center', marginTop: 4, color: dshThemeColor.labelTertiary, textAlign: 'center', fontSize: 12, lineHeight: 1.5 } }, createElement('summary', { style: { cursor: 'pointer', color: dshThemeColor.labelSecondary, listStylePosition: 'inside' } }, t('settings.version', { version: CODINGNS_VERSION })), createElement('div', { style: { display: 'flex', flexDirection: 'column', gap: 4, alignItems: 'center', maxWidth: 'min(100%, 560px)', marginBottom: 8, padding: '8px 12px', border: `1px solid ${dshThemeColor.border}`, borderRadius: 6, background: dshThemeColor.surfaceSubtle } }, createElement('div', undefined, t('settings.compatibility', { range: DSH_COMPATIBILITY })), createElement('a', { href: CODINGNS_GITHUB_URL, target: '_blank', rel: 'noreferrer', style: { color: dshThemeColor.accent, overflowWrap: 'anywhere' } }, CODINGNS_GITHUB_URL))));
|
|
23
37
|
}
|
|
24
38
|
/** 通用功能模块卡片:标题栏开关由 descriptor.ui 决定,内容由模块自己提供。 */
|
|
25
|
-
function FeatureCard({ entry, snapshot, services, restartStates }) {
|
|
39
|
+
function FeatureCard({ entry, snapshot, services, restartStates, notify }) {
|
|
26
40
|
const { module, ui } = entry;
|
|
27
41
|
const t = useCodingNsTranslator(services.locale);
|
|
28
|
-
const [writeError, setWriteError] = useState(null);
|
|
29
42
|
const versionCompatible = isFeatureDshVersionCompatible(module.descriptor, services.dshVersion);
|
|
30
43
|
const requestedEnabled = isFeatureEnabled(module.descriptor, snapshot.value);
|
|
31
44
|
const enabled = versionCompatible && requestedEnabled;
|
|
32
|
-
const effectiveEnabled = module.descriptor.activation === 'restart'
|
|
33
|
-
? versionCompatible && (restartStates[module.descriptor.name] ?? module.descriptor.enabledByDefault)
|
|
34
|
-
: enabled;
|
|
35
45
|
const panel = module.settingsPanel;
|
|
36
46
|
// 常驻模块不提供关闭入口;设置未就绪或只读时也不允许切换。
|
|
37
47
|
const switchDisabled = ui.alwaysEnabled === true || !versionCompatible || snapshot.status === 'loading' || !snapshot.writable;
|
|
38
48
|
const toggle = (next) => {
|
|
39
|
-
setWriteError(null);
|
|
40
49
|
if (!versionCompatible) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
50
|
+
notify({ kind: 'error', message: t('settings.versionBlocked', {
|
|
51
|
+
version: services.dshVersion,
|
|
52
|
+
minimum: module.descriptor.minimumDshVersion ?? '未知版本',
|
|
53
|
+
}) });
|
|
45
54
|
return;
|
|
46
55
|
}
|
|
47
56
|
void services.settings
|
|
48
57
|
.mutate([{ op: 'set', path: [CODINGNS_MODULES_FIELD, module.descriptor.name], value: next }])
|
|
58
|
+
.then((accepted) => {
|
|
59
|
+
if (!accepted) {
|
|
60
|
+
notify({ kind: 'error', message: t('settings.moduleWriteRejected') });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
notify({ kind: 'success', message: t(next ? 'settings.moduleEnabled' : 'settings.moduleDisabled', { label: t(ui.labelKey ?? ui.label) }) });
|
|
64
|
+
})
|
|
49
65
|
.catch((cause) => {
|
|
50
|
-
|
|
66
|
+
notify({ kind: 'error', message: cause instanceof Error ? cause.message : String(cause) });
|
|
51
67
|
});
|
|
52
68
|
};
|
|
53
69
|
return createElement('details', {
|
|
@@ -67,9 +83,7 @@ function FeatureCard({ entry, snapshot, services, restartStates }) {
|
|
|
67
83
|
version: services.dshVersion,
|
|
68
84
|
minimum: module.descriptor.minimumDshVersion ?? '未知版本',
|
|
69
85
|
}))
|
|
70
|
-
: null,
|
|
71
|
-
? createElement('span', undefined, t('settings.runtimeReported'))
|
|
72
|
-
: createElement('strong', undefined, t('settings.restartRequired'))), panel === undefined ? null : createElement(panel, { services, enabled, snapshot }), writeError === null ? null : createElement('div', { role: 'alert', style: { color: dshThemeColor.error } }, writeError)));
|
|
86
|
+
: null, panel === undefined ? null : createElement(panel, { services, enabled, snapshot, notify })));
|
|
73
87
|
}
|
|
74
88
|
/** 标题栏开关:真实 checkbox 语义,点击不会连带折叠卡片。 */
|
|
75
89
|
function FeatureSwitch({ label, checked, disabled, onChange }) {
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import type { ReactElement } from 'react';
|
|
2
2
|
import type { SlotRegistry } from '@deepseek-ai/dsh-client-ui-renderer/client';
|
|
3
3
|
import type { CodingNsRpcClient } from './features/types.js';
|
|
4
|
+
import type { SessionSnapshot } from './cli-slots.js';
|
|
4
5
|
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
5
6
|
interface SessionStandardProps {
|
|
6
7
|
sessionId: string;
|
|
7
8
|
}
|
|
8
9
|
}
|
|
10
|
+
type SessionSelector = <Selected>(selector: (value: SessionSnapshot) => Selected) => Selected;
|
|
9
11
|
interface SubscriptionSlotProps {
|
|
10
12
|
readonly rpc: CodingNsRpcClient;
|
|
11
13
|
readonly sessionId?: string;
|
|
14
|
+
readonly useSession?: SessionSelector;
|
|
12
15
|
}
|
|
13
16
|
/** 在 DSH 原生步骤统计左侧显示当前 Agent 的订阅余量。 */
|
|
14
17
|
export declare function registerSubscriptionSlot(slots: SlotRegistry, rpc: CodingNsRpcClient): () => void;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createElement, useEffect, useRef, useState } from 'react';
|
|
2
2
|
import { callCliRpc } from './cli-catalog.js';
|
|
3
|
+
import { providerIconUrl } from './provider-icons.js';
|
|
3
4
|
import { dshPopupSurfaceStyle, dshThemeColor } from './theme.js';
|
|
4
5
|
/** 在 DSH 原生步骤统计左侧显示当前 Agent 的订阅余量。 */
|
|
5
6
|
export function registerSubscriptionSlot(slots, rpc) {
|
|
@@ -14,17 +15,20 @@ export function registerSubscriptionSlot(slots, rpc) {
|
|
|
14
15
|
function CommandCodeSubscriptionSlot(props) {
|
|
15
16
|
const [usage, setUsage] = useState(null);
|
|
16
17
|
const [adapterId, setAdapterId] = useState(null);
|
|
18
|
+
const [providerId, setProviderId] = useState(null);
|
|
17
19
|
const [eligible, setEligible] = useState(false);
|
|
18
20
|
const [open, setOpen] = useState(false);
|
|
19
21
|
const [loading, setLoading] = useState(false);
|
|
20
22
|
const [clock, setClock] = useState(() => Date.now());
|
|
21
23
|
const rootRef = useRef(null);
|
|
24
|
+
const modelSelectionRevision = props.useSession?.((value) => JSON.stringify(value.modelSelection));
|
|
22
25
|
useEffect(() => {
|
|
23
26
|
const sessionId = props.sessionId?.trim();
|
|
24
27
|
if (!sessionId) {
|
|
25
28
|
setEligible(false);
|
|
26
29
|
setUsage(null);
|
|
27
30
|
setAdapterId(null);
|
|
31
|
+
setProviderId(null);
|
|
28
32
|
setOpen(false);
|
|
29
33
|
return;
|
|
30
34
|
}
|
|
@@ -32,6 +36,7 @@ function CommandCodeSubscriptionSlot(props) {
|
|
|
32
36
|
setEligible(false);
|
|
33
37
|
setUsage(null);
|
|
34
38
|
setAdapterId(null);
|
|
39
|
+
setProviderId(null);
|
|
35
40
|
setOpen(false);
|
|
36
41
|
const refresh = async () => {
|
|
37
42
|
setLoading(true);
|
|
@@ -43,14 +48,19 @@ function CommandCodeSubscriptionSlot(props) {
|
|
|
43
48
|
setEligible(false);
|
|
44
49
|
setUsage(null);
|
|
45
50
|
setAdapterId(null);
|
|
51
|
+
setProviderId(null);
|
|
46
52
|
}
|
|
47
53
|
return;
|
|
48
54
|
}
|
|
49
55
|
if (active) {
|
|
50
56
|
setEligible(true);
|
|
51
57
|
setAdapterId(adapterId);
|
|
58
|
+
setProviderId(selection.providerId ?? null);
|
|
52
59
|
}
|
|
53
|
-
const next = await callCliRpc(props.rpc, 'subscription', {
|
|
60
|
+
const next = await callCliRpc(props.rpc, 'subscription', {
|
|
61
|
+
adapterId,
|
|
62
|
+
...(selection.providerId ? { providerId: selection.providerId } : {}),
|
|
63
|
+
});
|
|
54
64
|
if (active)
|
|
55
65
|
setUsage(next);
|
|
56
66
|
}
|
|
@@ -71,7 +81,7 @@ function CommandCodeSubscriptionSlot(props) {
|
|
|
71
81
|
active = false;
|
|
72
82
|
globalThis.clearInterval(timer);
|
|
73
83
|
};
|
|
74
|
-
}, [props.rpc, props.sessionId]);
|
|
84
|
+
}, [props.rpc, props.sessionId, modelSelectionRevision]);
|
|
75
85
|
useEffect(() => {
|
|
76
86
|
if (!eligible || usage === null)
|
|
77
87
|
return;
|
|
@@ -98,17 +108,33 @@ function CommandCodeSubscriptionSlot(props) {
|
|
|
98
108
|
};
|
|
99
109
|
}, [open]);
|
|
100
110
|
// 未拿到真实订阅数据时不占用底部栏空间;加载状态不能伪装成订阅存在。
|
|
101
|
-
if (!eligible || usage === null || (usage.sub2api === undefined && resolveDisplayWindow(usage) === null))
|
|
111
|
+
if (!eligible || usage === null || (usage.sub2api === undefined && usage.deepseek === undefined && usage.providerBalance === undefined && resolveDisplayWindow(usage) === null))
|
|
102
112
|
return null;
|
|
103
113
|
const sub2api = usage.sub2api;
|
|
104
|
-
const
|
|
114
|
+
const deepseek = usage.deepseek;
|
|
115
|
+
const providerBalance = usage.providerBalance;
|
|
116
|
+
const displayWindow = sub2api === undefined && deepseek === undefined && providerBalance === undefined ? resolveDisplayWindow(usage) : null;
|
|
105
117
|
const remaining = displayWindow?.remainingPercent ?? null;
|
|
106
118
|
const resetLabel = displayWindow === null ? null : formatCountdown(displayWindow.resetsAt, clock);
|
|
107
|
-
const providerName = subscriptionProviderName(adapterId);
|
|
108
|
-
const
|
|
119
|
+
const providerName = subscriptionProviderName(adapterId, providerId, usage);
|
|
120
|
+
const deepseekBalance = deepseek === undefined ? null : selectDeepseekBalance(deepseek);
|
|
121
|
+
const providerLogoSource = usage.provider?.logoDataUrl ?? (isRemoteWebContext() ? '' : usage.provider?.logoUrl ?? '');
|
|
122
|
+
const deepseekIconSource = providerLogoSource || (providerBalance === undefined ? providerIconUrl('dsh') : '');
|
|
123
|
+
const label = sub2api === undefined && deepseek === undefined && providerBalance === undefined
|
|
109
124
|
? `${providerName} 订阅余量 ${formatPercent(remaining ?? 0)}%`
|
|
110
|
-
:
|
|
111
|
-
|
|
125
|
+
: sub2api !== undefined
|
|
126
|
+
? `${providerName} 上游余额 ${formatSub2ApiMoney(sub2api.balance, sub2api.unit)}`
|
|
127
|
+
: deepseek !== undefined
|
|
128
|
+
? `${providerName} 余额 ${deepseekBalance === null ? '不可用' : formatDeepseekMoney(deepseekBalance.totalBalance, deepseekBalance.currency)}`
|
|
129
|
+
: `${providerName} 余量 ${formatProviderBalance(providerBalance)}`;
|
|
130
|
+
const logoSource = providerLogoSource || (sub2api === undefined ? '' : (sub2api.logoDataUrl ?? (isRemoteWebContext() ? '' : sub2api.logoUrl)));
|
|
131
|
+
const triggerContent = sub2api === undefined && deepseek === undefined && providerBalance === undefined
|
|
132
|
+
? createElement('span', { 'aria-hidden': true, style: progressRingStyle() }, createElement('span', { style: { ...progressRingVisualStyle, background: progressRingVisualBackground(remaining === null ? 0 : remaining / 100, false) } }, createElement('span', { style: progressRingValueStyle }, createElement('span', undefined, formatRingPercentage(remaining ?? 0)), createElement('span', { style: progressRingSuffixStyle }, '%'))))
|
|
133
|
+
: sub2api !== undefined
|
|
134
|
+
? createElement('span', { 'aria-hidden': true, style: sub2apiIdentityStyle }, logoSource !== '' && createElement('img', { src: logoSource, alt: '', width: 20, height: 20, style: sub2apiLogoStyle }), createElement('span', undefined, formatSub2ApiMoney(sub2api.balance, sub2api.unit)))
|
|
135
|
+
: deepseek !== undefined
|
|
136
|
+
? createElement('span', { 'aria-hidden': true, style: deepseekBalanceIdentityStyle }, deepseekIconSource !== undefined && createElement('img', { src: deepseekIconSource, alt: '', width: 18, height: 18, style: deepseekLogoStyle }), createElement('span', { style: deepseekBalanceStyle }, deepseekBalance === null ? '--' : formatDeepseekMoney(deepseekBalance.totalBalance, deepseekBalance.currency)))
|
|
137
|
+
: createElement('span', { 'aria-hidden': true, style: deepseekBalanceIdentityStyle }, deepseekIconSource !== undefined && createElement('img', { src: deepseekIconSource, alt: '', width: 18, height: 18, style: deepseekLogoStyle }), createElement('span', { style: deepseekBalanceStyle }, formatProviderBalance(providerBalance)));
|
|
112
138
|
return createElement('div', { ref: rootRef, style: subscriptionRootStyle }, createElement('button', {
|
|
113
139
|
type: 'button',
|
|
114
140
|
onClick: () => setOpen((value) => !value),
|
|
@@ -116,19 +142,42 @@ function CommandCodeSubscriptionSlot(props) {
|
|
|
116
142
|
'aria-label': label,
|
|
117
143
|
'aria-expanded': open,
|
|
118
144
|
style: subscriptionTriggerStyle,
|
|
119
|
-
}, sub2api === undefined
|
|
120
|
-
?
|
|
121
|
-
:
|
|
145
|
+
}, triggerContent, createElement('span', { style: subscriptionLabelStyle }, sub2api === undefined && deepseek === undefined && providerBalance === undefined
|
|
146
|
+
? (resetLabel ?? '订阅余量')
|
|
147
|
+
: sub2api !== undefined ? `今日 ${formatSub2ApiMoney(sub2api.today.cost, sub2api.unit)}` : deepseek !== undefined ? '账户余额' : '官方余量')), open && createElement(SubscriptionPopover, { usage, providerName }));
|
|
122
148
|
}
|
|
123
149
|
function SubscriptionPopover({ usage, providerName }) {
|
|
124
150
|
if (usage.sub2api !== undefined)
|
|
125
151
|
return createElement(Sub2ApiPopover, { usage: usage.sub2api, providerName });
|
|
152
|
+
if (usage.deepseek !== undefined)
|
|
153
|
+
return createElement(DeepseekPopover, { usage: usage.deepseek, providerName });
|
|
154
|
+
if (usage.providerBalance !== undefined)
|
|
155
|
+
return createElement(ProviderBalancePopover, { usage: usage.providerBalance, providerName });
|
|
126
156
|
const windows = [
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
157
|
+
{ id: 'primary', label: formatSubscriptionWindowLabel(usage.primary, '5 小时额度'), window: usage.primary },
|
|
158
|
+
{ id: 'secondary', label: formatSubscriptionWindowLabel(usage.secondary, '周额度'), window: usage.secondary },
|
|
159
|
+
{ id: 'monthly', label: formatSubscriptionWindowLabel(usage.monthly, '月额度'), window: usage.monthly },
|
|
130
160
|
];
|
|
131
|
-
return createElement('div', { role: 'dialog', 'aria-label': `${providerName} 订阅使用情况`, style: subscriptionPopoverStyle }, createElement('div', { style: popoverHeadingStyle }, createElement('strong', undefined, `${providerName} 订阅`), usage.planType && createElement('span', { style: { color: dshThemeColor.labelTertiary } }, formatPlanType(usage.planType))), ...windows.map((
|
|
161
|
+
return createElement('div', { role: 'dialog', 'aria-label': `${providerName} 订阅使用情况`, style: subscriptionPopoverStyle }, createElement('div', { style: popoverHeadingStyle }, createElement('strong', undefined, `${providerName} 订阅`), usage.planType && createElement('span', { style: { color: dshThemeColor.labelTertiary } }, formatPlanType(usage.planType))), ...windows.map(({ id, label, window }) => window === null ? null : createElement('section', { key: id, style: windowStyle }, createElement('div', { style: windowHeadingStyle }, createElement('span', undefined, label), createElement('span', undefined, `${formatPercent(window.remainingPercent)}%`)), createElement('div', { role: 'progressbar', 'aria-label': `${label}剩余 ${formatPercent(window.remainingPercent)}%`, 'aria-valuemin': 0, 'aria-valuemax': 100, 'aria-valuenow': window.remainingPercent, style: barStyle }, createElement('span', { style: { ...barFillStyle, width: `${window.remainingPercent}%` } })), window.resetsAt !== null && createElement('div', { style: resetStyle }, `重置于 ${formatCountdown(window.resetsAt)}`))));
|
|
162
|
+
}
|
|
163
|
+
/** 根据服务端返回的窗口时长生成准确的额度标签,避免把七天窗口误显示成五小时。 */
|
|
164
|
+
function formatSubscriptionWindowLabel(window, fallback) {
|
|
165
|
+
const durationMins = window?.windowDurationMins;
|
|
166
|
+
if (durationMins === null || durationMins === undefined || !Number.isFinite(durationMins) || durationMins <= 0)
|
|
167
|
+
return fallback;
|
|
168
|
+
if (durationMins % (24 * 60) === 0)
|
|
169
|
+
return `${durationMins / (24 * 60)} 天额度`;
|
|
170
|
+
if (durationMins % 60 === 0)
|
|
171
|
+
return `${durationMins / 60} 小时额度`;
|
|
172
|
+
return `${durationMins} 分钟额度`;
|
|
173
|
+
}
|
|
174
|
+
function ProviderBalancePopover({ usage, providerName }) {
|
|
175
|
+
return createElement('div', { role: 'dialog', 'aria-label': `${providerName} 官方余量`, style: subscriptionPopoverStyle }, createElement('div', { style: popoverHeadingStyle }, createElement('strong', undefined, `${providerName} 官方余量`), createElement('span', { style: { color: dshThemeColor.labelTertiary } }, formatProviderBalance(usage))), usage.used !== null && usage.total !== null && createElement('div', { style: upstreamMetaStyle }, `已用 ${formatProviderBalanceValue(usage.used, usage.unit)} / ${formatProviderBalanceValue(usage.total, usage.unit)}`), usage.details.length === 0
|
|
176
|
+
? createElement('div', { style: resetStyle }, '暂无更多统计')
|
|
177
|
+
: usage.details.map((item) => createElement('div', { key: item.label, style: deepseekBalanceDetailsStyle }, createElement('span', undefined, item.label), createElement('span', undefined, String(item.value)))));
|
|
178
|
+
}
|
|
179
|
+
function DeepseekPopover({ usage, providerName }) {
|
|
180
|
+
return createElement('div', { role: 'dialog', 'aria-label': `${providerName} 账户余额`, style: subscriptionPopoverStyle }, createElement('div', { style: popoverHeadingStyle }, createElement('strong', undefined, `${providerName} 账户余额`), createElement('span', { style: { color: usage.isAvailable === false ? dshThemeColor.error : dshThemeColor.labelTertiary } }, usage.isAvailable === false ? '不可用' : '可用')), usage.balances.map((balance) => createElement('section', { key: balance.currency, style: deepseekBalanceSectionStyle }, createElement('div', { style: windowHeadingStyle }, createElement('span', undefined, balance.currency), createElement('strong', undefined, formatDeepseekMoney(balance.totalBalance, balance.currency))), createElement('div', { style: deepseekBalanceDetailsStyle }, createElement('span', undefined, `赠送 ${formatDeepseekMoney(balance.grantedBalance, balance.currency)}`), createElement('span', undefined, `充值 ${formatDeepseekMoney(balance.toppedUpBalance, balance.currency)}`)))), createElement('div', { style: deepseekUnavailableStatsStyle }, '官方 DeepSeek API 当前只提供账户余额接口,暂无请求量、Token 或费用明细。'));
|
|
132
181
|
}
|
|
133
182
|
function Sub2ApiPopover({ usage, providerName }) {
|
|
134
183
|
return createElement('div', { role: 'dialog', 'aria-label': `${providerName} 上游用量`, style: subscriptionPopoverStyle }, createElement('div', { style: popoverHeadingStyle }, createElement('strong', undefined, `${providerName} 上游用量`), createElement('span', { style: { color: dshThemeColor.labelTertiary } }, formatSub2ApiMoney(usage.balance, usage.unit))), createElement('div', { style: upstreamMetaStyle }, createElement('span', { style: upstreamTypeStyle }, usage.upstreamType), usage.upstreamUrl === ''
|
|
@@ -146,13 +195,25 @@ function createModelRow(model, unit) {
|
|
|
146
195
|
function resolveDisplayWindow(usage) {
|
|
147
196
|
return usage.primary ?? usage.secondary ?? usage.monthly;
|
|
148
197
|
}
|
|
198
|
+
function selectDeepseekBalance(usage) {
|
|
199
|
+
return usage.balances.find((balance) => balance.currency.toUpperCase() === 'USD') ?? usage.balances[0] ?? null;
|
|
200
|
+
}
|
|
149
201
|
function isSubscriptionAdapter(adapterId) {
|
|
150
202
|
return adapterId === 'command-code' || adapterId === 'codex' || adapterId === 'claude-code' || adapterId === 'dsh' || adapterId === 'grok' || adapterId === 'opencode';
|
|
151
203
|
}
|
|
152
204
|
function isRemoteWebContext() {
|
|
153
205
|
return globalThis.__CODINGNS4DSH_REMOTE_WEB_CONTEXT__ === true;
|
|
154
206
|
}
|
|
155
|
-
function subscriptionProviderName(adapterId) {
|
|
207
|
+
function subscriptionProviderName(adapterId, providerId, usage) {
|
|
208
|
+
if (usage.provider?.displayName)
|
|
209
|
+
return usage.provider.displayName;
|
|
210
|
+
if (adapterId === 'dsh' && providerId !== null) {
|
|
211
|
+
if (/^(?:deepseek(?:-official)?|official-deepseek)$/iu.test(providerId))
|
|
212
|
+
return 'DeepSeek 官方';
|
|
213
|
+
return formatProviderName(providerId);
|
|
214
|
+
}
|
|
215
|
+
if (adapterId === 'dsh' && usage.sub2api !== undefined)
|
|
216
|
+
return `${usage.sub2api.upstreamType} 上游`;
|
|
156
217
|
switch (adapterId) {
|
|
157
218
|
case 'command-code': return 'Command Code';
|
|
158
219
|
case 'codex': return 'Codex';
|
|
@@ -163,6 +224,9 @@ function subscriptionProviderName(adapterId) {
|
|
|
163
224
|
default: return 'Agent';
|
|
164
225
|
}
|
|
165
226
|
}
|
|
227
|
+
function formatProviderName(value) {
|
|
228
|
+
return value.replace(/[-_]+/gu, ' ').replace(/(^|\s)([a-z])/gu, (_match, prefix, letter) => `${prefix}${letter.toUpperCase()}`);
|
|
229
|
+
}
|
|
166
230
|
function formatPercent(value) { return Math.max(0, Math.min(100, value)).toFixed(0); }
|
|
167
231
|
function formatRingPercentage(value) { return String(Math.floor(Math.max(0, Math.min(100, value)))); }
|
|
168
232
|
function formatSub2ApiMoney(value, unit) {
|
|
@@ -171,6 +235,28 @@ function formatSub2ApiMoney(value, unit) {
|
|
|
171
235
|
return `$${value.toFixed(2)}`;
|
|
172
236
|
return `${value.toFixed(2)}${normalizedUnit === '' ? '' : ` ${normalizedUnit}`}`;
|
|
173
237
|
}
|
|
238
|
+
function formatDeepseekMoney(value, currency) {
|
|
239
|
+
const normalizedCurrency = currency.trim().toUpperCase();
|
|
240
|
+
const amount = value.toFixed(2);
|
|
241
|
+
if (normalizedCurrency === 'USD')
|
|
242
|
+
return `$${amount}`;
|
|
243
|
+
if (normalizedCurrency === 'CNY')
|
|
244
|
+
return `¥${amount}`;
|
|
245
|
+
return `${amount} ${normalizedCurrency}`;
|
|
246
|
+
}
|
|
247
|
+
function formatProviderBalance(usage) {
|
|
248
|
+
if (usage === undefined || usage.remaining === null)
|
|
249
|
+
return '--';
|
|
250
|
+
return formatProviderBalanceValue(usage.remaining, usage.unit);
|
|
251
|
+
}
|
|
252
|
+
function formatProviderBalanceValue(value, unit) {
|
|
253
|
+
const normalized = unit?.trim().toUpperCase() ?? '';
|
|
254
|
+
if (normalized === '%')
|
|
255
|
+
return `${value.toFixed(0)}%`;
|
|
256
|
+
if (normalized === 'USD')
|
|
257
|
+
return `$${value.toFixed(2)}`;
|
|
258
|
+
return `${value.toFixed(2)}${normalized === '' ? '' : ` ${normalized}`}`;
|
|
259
|
+
}
|
|
174
260
|
function formatSub2ApiTokens(value) { return new Intl.NumberFormat('zh-CN', { notation: 'compact', maximumFractionDigits: 1 }).format(value); }
|
|
175
261
|
function formatSub2ApiPercent(value) { return `${Math.max(0, Math.min(100, value)).toFixed(1)}%`; }
|
|
176
262
|
function formatInteger(value) { return new Intl.NumberFormat('zh-CN').format(Math.max(0, Math.round(value))); }
|
|
@@ -193,6 +279,12 @@ const subscriptionTriggerStyle = { display: 'inline-flex', alignItems: 'center',
|
|
|
193
279
|
const subscriptionLabelStyle = { whiteSpace: 'nowrap' };
|
|
194
280
|
const sub2apiIdentityStyle = { display: 'inline-flex', alignItems: 'center', gap: 5, whiteSpace: 'nowrap', fontVariantNumeric: 'tabular-nums' };
|
|
195
281
|
const sub2apiLogoStyle = { display: 'block', borderRadius: 4, objectFit: 'contain' };
|
|
282
|
+
const deepseekBalanceIdentityStyle = { display: 'inline-flex', alignItems: 'center', gap: 5, whiteSpace: 'nowrap', fontVariantNumeric: 'tabular-nums' };
|
|
283
|
+
const deepseekLogoStyle = { display: 'block', borderRadius: 5, objectFit: 'contain' };
|
|
284
|
+
const deepseekBalanceStyle = { display: 'inline-flex', alignItems: 'center', color: dshThemeColor.labelSecondary, fontSize: 13, fontWeight: 600, fontVariantNumeric: 'tabular-nums' };
|
|
285
|
+
const deepseekBalanceSectionStyle = { display: 'grid', gap: 6, marginTop: 10, padding: '10px 0 2px', borderTop: `1px solid ${dshThemeColor.border}` };
|
|
286
|
+
const deepseekBalanceDetailsStyle = { display: 'flex', justifyContent: 'space-between', gap: 12, color: dshThemeColor.labelTertiary, fontSize: 12 };
|
|
287
|
+
const deepseekUnavailableStatsStyle = { marginTop: 12, paddingTop: 10, borderTop: `1px solid ${dshThemeColor.border}`, color: dshThemeColor.labelTertiary, fontSize: 12, lineHeight: '17px' };
|
|
196
288
|
const progressRingVisualStyle = { boxSizing: 'border-box', width: '100%', height: '100%', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', padding: 2, borderRadius: 'inherit' };
|
|
197
289
|
const progressRingValueStyle = { boxSizing: 'border-box', width: '100%', height: '100%', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 0.5, borderRadius: 'inherit', background: dshThemeColor.menuBackground, fontSize: 7, lineHeight: 1, fontWeight: 700, color: dshThemeColor.labelPrimary, whiteSpace: 'nowrap' };
|
|
198
290
|
const progressRingSuffixStyle = { fontSize: 5.5, lineHeight: 1, color: dshThemeColor.labelTertiary, transform: 'translateY(1px)' };
|
|
@@ -48,15 +48,20 @@ export interface TerminalObservable<T> {
|
|
|
48
48
|
getSnapshot(): T;
|
|
49
49
|
subscribe(listener: () => void): () => void;
|
|
50
50
|
}
|
|
51
|
+
interface WorkspaceBindingResolution {
|
|
52
|
+
readonly id: WebTerminalId;
|
|
53
|
+
/** 是否来自已有工作区绑定;新建绑定仍允许本次 view 创建 Host 终端。 */
|
|
54
|
+
readonly existing: boolean;
|
|
55
|
+
}
|
|
51
56
|
/** 一个 Sidebar 标签对应的终端模型;DOM 卸载只 detach,显式 close 才结束 Host 进程。 */
|
|
52
57
|
export declare class CodingNsTerminalView {
|
|
53
58
|
readonly sessionId: string;
|
|
54
|
-
readonly id: WebTerminalId;
|
|
55
59
|
private readonly remote;
|
|
56
60
|
private readonly createWhenMissing;
|
|
57
61
|
private readonly shellPath?;
|
|
58
62
|
private readonly onWorkspaceResolved?;
|
|
59
63
|
readonly state: TerminalObservable<TerminalViewState>;
|
|
64
|
+
id: WebTerminalId;
|
|
60
65
|
private readonly store;
|
|
61
66
|
private readonly lifetime;
|
|
62
67
|
private followController;
|
|
@@ -68,7 +73,7 @@ export declare class CodingNsTerminalView {
|
|
|
68
73
|
private attachmentId;
|
|
69
74
|
/** 最近一次已排队的尺寸;ResizeObserver 可能在同一布局周期内重复触发。 */
|
|
70
75
|
private lastResize;
|
|
71
|
-
constructor(sessionId: string, id: WebTerminalId, remote: TerminalRemoteSource, createWhenMissing: boolean, shellPath?: string | undefined, onWorkspaceResolved?: ((workspaceId: string) =>
|
|
76
|
+
constructor(sessionId: string, id: WebTerminalId, remote: TerminalRemoteSource, createWhenMissing: boolean, shellPath?: string | undefined, onWorkspaceResolved?: ((workspaceId: string, id: WebTerminalId) => WorkspaceBindingResolution | undefined) | undefined);
|
|
72
77
|
mount(): () => void;
|
|
73
78
|
refresh(): Promise<void>;
|
|
74
79
|
acknowledge(revision: number): void;
|
|
@@ -93,7 +98,11 @@ export declare class CodingNsWebTerminals extends Service {
|
|
|
93
98
|
private readonly recoveries;
|
|
94
99
|
private readonly closeFailureStore;
|
|
95
100
|
readonly closeFailures: TerminalObservable<readonly TerminalCloseFailure[]>;
|
|
101
|
+
private readonly remoteReadyStore;
|
|
102
|
+
readonly remoteReadyState: TerminalObservable<boolean>;
|
|
96
103
|
private readonly closeRequests;
|
|
104
|
+
/** Remote 注入前不能执行关闭请求;就绪后统一冲刷,避免把启动竞态显示成永久错误。 */
|
|
105
|
+
private cleanupQueued;
|
|
97
106
|
constructor(ctx: Context, remote: TerminalRemoteSource);
|
|
98
107
|
view(sessionId: string, key: string, contentId: string, terminalId?: WebTerminalId, shellPath?: string): CodingNsTerminalView;
|
|
99
108
|
/** 返回某个 Sidebar 内容已经保存的 Host 终端身份,用于恢复时去重。 */
|
|
@@ -103,9 +112,12 @@ export declare class CodingNsWebTerminals extends Service {
|
|
|
103
112
|
close(sessionId: string, key: string, contentId: string, terminalId?: WebTerminalId): void;
|
|
104
113
|
recover(sessionId: string): Promise<readonly WebTerminalInfo[]>;
|
|
105
114
|
retryClose(id: WebTerminalId): void;
|
|
115
|
+
/** 由 Client 的 Remote 注入回调调用,完成启动阶段延迟的关闭请求。 */
|
|
116
|
+
remoteReady(): void;
|
|
106
117
|
dispose(): Promise<void>;
|
|
107
118
|
private rememberWorkspace;
|
|
108
119
|
private cleanup;
|
|
120
|
+
private flushCleanup;
|
|
109
121
|
}
|
|
110
122
|
declare module '@deepseek-ai/cordis' {
|
|
111
123
|
interface Context {
|