@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.
Files changed (213) hide show
  1. package/README.en.md +306 -0
  2. package/README.md +190 -993
  3. package/data/build/dist/client/account-bar.d.ts +2 -2
  4. package/data/build/dist/client/account-bar.js +96 -39
  5. package/data/build/dist/client/bundle.js +13722 -3611
  6. package/data/build/dist/client/cli-catalog.js +20 -3
  7. package/data/build/dist/client/cli-slots.d.ts +5 -0
  8. package/data/build/dist/client/cli-slots.js +57 -7
  9. package/data/build/dist/client/debug/ui.d.ts +1 -1
  10. package/data/build/dist/client/debug/ui.js +62 -32
  11. package/data/build/dist/client/dsh-runtime-version.d.ts +9 -2
  12. package/data/build/dist/client/dsh-runtime-version.js +24 -2
  13. package/data/build/dist/client/features/cli-adapters.d.ts +1 -1
  14. package/data/build/dist/client/features/cli-adapters.js +19 -16
  15. package/data/build/dist/client/features/debug.js +2 -2
  16. package/data/build/dist/client/features/index.d.ts +2 -1
  17. package/data/build/dist/client/features/index.js +3 -1
  18. package/data/build/dist/client/features/lan-access-panel.d.ts +1 -1
  19. package/data/build/dist/client/features/lan-access-panel.js +8 -10
  20. package/data/build/dist/client/features/lan-access.js +1 -1
  21. package/data/build/dist/client/features/login-protection-panel.d.ts +1 -1
  22. package/data/build/dist/client/features/login-protection-panel.js +7 -9
  23. package/data/build/dist/client/features/reverse-proxy-panel.d.ts +1 -1
  24. package/data/build/dist/client/features/reverse-proxy-panel.js +99 -11
  25. package/data/build/dist/client/features/reverse-proxy.d.ts +1 -1
  26. package/data/build/dist/client/features/reverse-proxy.js +19 -18
  27. package/data/build/dist/client/features/terminal-enhancement-panel.d.ts +1 -1
  28. package/data/build/dist/client/features/terminal-enhancement-panel.js +62 -55
  29. package/data/build/dist/client/features/terminal-enhancement.d.ts +1 -1
  30. package/data/build/dist/client/features/terminal-enhancement.js +3 -3
  31. package/data/build/dist/client/features/types.d.ts +14 -3
  32. package/data/build/dist/client/features/workspace-session-enhancement-panel.d.ts +1 -1
  33. package/data/build/dist/client/features/workspace-session-enhancement-panel.js +31 -9
  34. package/data/build/dist/client/features/workspace-session-enhancement.js +72 -11
  35. package/data/build/dist/client/git-management.d.ts +16 -0
  36. package/data/build/dist/client/git-management.js +757 -0
  37. package/data/build/dist/client/index.d.ts +3 -2
  38. package/data/build/dist/client/index.js +152 -27
  39. package/data/build/dist/client/locale.js +91 -29
  40. package/data/build/dist/client/quick-phrase-layout.d.ts +49 -0
  41. package/data/build/dist/client/quick-phrase-layout.js +95 -0
  42. package/data/build/dist/client/quick-phrase-slot.d.ts +7 -0
  43. package/data/build/dist/client/quick-phrase-slot.js +370 -0
  44. package/data/build/dist/client/remote-web-context.js +1 -1
  45. package/data/build/dist/client/settings-bridge.d.ts +9 -6
  46. package/data/build/dist/client/settings-bridge.js +51 -27
  47. package/data/build/dist/client/settings-section.d.ts +3 -2
  48. package/data/build/dist/client/settings-section.js +33 -19
  49. package/data/build/dist/client/subscription-slot.d.ts +3 -0
  50. package/data/build/dist/client/subscription-slot.js +108 -16
  51. package/data/build/dist/client/terminal/model.d.ts +14 -2
  52. package/data/build/dist/client/terminal/model.js +109 -21
  53. package/data/build/dist/client/terminal/recovery.d.ts +50 -0
  54. package/data/build/dist/client/terminal/recovery.js +101 -0
  55. package/data/build/dist/client/terminal/ui.d.ts +2 -2
  56. package/data/build/dist/client/terminal/ui.js +49 -11
  57. package/data/build/dist/client/terminal/xterm-view.d.ts +2 -2
  58. package/data/build/dist/client/terminal/xterm-view.js +2 -1
  59. package/data/build/dist/client/theme.d.ts +6 -1
  60. package/data/build/dist/client/theme.js +32 -1
  61. package/data/build/dist/client/workspace-session-archive-dom.d.ts +12 -0
  62. package/data/build/dist/client/workspace-session-archive-dom.js +27 -5
  63. package/data/build/dist/client/workspace-session-visibility-dom.d.ts +30 -0
  64. package/data/build/dist/client/workspace-session-visibility-dom.js +604 -0
  65. package/data/build/dist/dsh-capabilities/client/config-forms-adapter.d.ts +50 -0
  66. package/data/build/dist/dsh-capabilities/client/config-forms-adapter.js +139 -0
  67. package/data/build/dist/dsh-capabilities/client/conversation-adapter.d.ts +7 -0
  68. package/data/build/dist/dsh-capabilities/client/conversation-adapter.js +6 -0
  69. package/data/build/dist/dsh-capabilities/client/locale-adapter.d.ts +9 -0
  70. package/data/build/dist/dsh-capabilities/client/locale-adapter.js +4 -0
  71. package/data/build/dist/dsh-capabilities/client/primitives-adapter.d.ts +10 -0
  72. package/data/build/dist/dsh-capabilities/client/primitives-adapter.js +14 -0
  73. package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.d.ts +8 -0
  74. package/data/build/dist/dsh-capabilities/client/settings-scope-adapter.js +16 -0
  75. package/data/build/dist/dsh-capabilities/client/sidebar-adapter.d.ts +7 -0
  76. package/data/build/dist/dsh-capabilities/client/sidebar-adapter.js +5 -0
  77. package/data/build/dist/dsh-capabilities/client/theme-adapter.d.ts +6 -0
  78. package/data/build/dist/dsh-capabilities/client/theme-adapter.js +4 -0
  79. package/data/build/dist/dsh-capabilities/client/typert-adapter.d.ts +5 -0
  80. package/data/build/dist/dsh-capabilities/client/typert-adapter.js +4 -0
  81. package/data/build/dist/dsh-capabilities/host/config-forms-adapter.d.ts +19 -0
  82. package/data/build/dist/dsh-capabilities/host/config-forms-adapter.js +45 -0
  83. package/data/build/dist/dsh-capabilities/host/connection-rpc-adapter.d.ts +19 -0
  84. package/data/build/dist/dsh-capabilities/host/connection-rpc-adapter.js +13 -0
  85. package/data/build/dist/dsh-capabilities/host/settings-scope-adapter.d.ts +8 -0
  86. package/data/build/dist/dsh-capabilities/host/settings-scope-adapter.js +47 -0
  87. package/data/build/dist/dsh-capabilities/host/typert-adapter.d.ts +6 -0
  88. package/data/build/dist/dsh-capabilities/host/typert-adapter.js +4 -0
  89. package/data/build/dist/dsh-capabilities/index.d.ts +6 -0
  90. package/data/build/dist/dsh-capabilities/index.js +6 -0
  91. package/data/build/dist/dsh-capabilities/matrix.d.ts +15 -0
  92. package/data/build/dist/dsh-capabilities/matrix.js +32 -0
  93. package/data/build/dist/dsh-capabilities/registry.d.ts +22 -0
  94. package/data/build/dist/dsh-capabilities/registry.js +196 -0
  95. package/data/build/dist/dsh-capabilities/routes.d.ts +10 -0
  96. package/data/build/dist/dsh-capabilities/routes.js +34 -0
  97. package/data/build/dist/dsh-capabilities/settings-store.d.ts +29 -0
  98. package/data/build/dist/dsh-capabilities/settings-store.js +5 -0
  99. package/data/build/dist/dsh-capabilities/types.d.ts +50 -0
  100. package/data/build/dist/dsh-capabilities/types.js +23 -0
  101. package/data/build/dist/features/registry.d.ts +8 -2
  102. package/data/build/dist/features/registry.js +104 -1
  103. package/data/build/dist/host/auth-session.d.ts +8 -1
  104. package/data/build/dist/host/auth-session.js +28 -6
  105. package/data/build/dist/host/cli-adapters/claude-driver.d.ts +2 -0
  106. package/data/build/dist/host/cli-adapters/claude-driver.js +19 -4
  107. package/data/build/dist/host/cli-adapters/claude-model-options.d.ts +14 -0
  108. package/data/build/dist/host/cli-adapters/claude-model-options.js +157 -0
  109. package/data/build/dist/host/cli-adapters/codex-driver.d.ts +11 -0
  110. package/data/build/dist/host/cli-adapters/codex-driver.js +166 -14
  111. package/data/build/dist/host/cli-adapters/driver.d.ts +2 -0
  112. package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +15 -0
  113. package/data/build/dist/host/cli-adapters/dsh-message-projector.js +120 -29
  114. package/data/build/dist/host/cli-adapters/dsh-tool-history.d.ts +4 -1
  115. package/data/build/dist/host/cli-adapters/dsh-tool-history.js +25 -7
  116. package/data/build/dist/host/cli-adapters/feature.js +74 -8
  117. package/data/build/dist/host/cli-adapters/gemini-driver.js +2 -2
  118. package/data/build/dist/host/cli-adapters/grok-driver.js +2 -2
  119. package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +4 -2
  120. package/data/build/dist/host/cli-adapters/json-rpc-process.js +8 -9
  121. package/data/build/dist/host/cli-adapters/kimi-driver.js +1 -1
  122. package/data/build/dist/host/cli-adapters/legacy-session-adapter.d.ts +17 -0
  123. package/data/build/dist/host/cli-adapters/legacy-session-adapter.js +88 -0
  124. package/data/build/dist/host/cli-adapters/legacy-session-settings.d.ts +24 -0
  125. package/data/build/dist/host/cli-adapters/legacy-session-settings.js +162 -0
  126. package/data/build/dist/host/cli-adapters/official-provider-subscription.d.ts +29 -0
  127. package/data/build/dist/host/cli-adapters/official-provider-subscription.js +361 -0
  128. package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +2 -0
  129. package/data/build/dist/host/cli-adapters/opencode-driver.js +97 -12
  130. package/data/build/dist/host/cli-adapters/pi-driver.js +69 -23
  131. package/data/build/dist/host/cli-adapters/provider-registry.d.ts +33 -0
  132. package/data/build/dist/host/cli-adapters/provider-registry.js +83 -0
  133. package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +25 -3
  134. package/data/build/dist/host/cli-adapters/provider-subscription.js +346 -30
  135. package/data/build/dist/host/cli-adapters/registry.d.ts +6 -0
  136. package/data/build/dist/host/cli-adapters/registry.js +170 -13
  137. package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +10 -0
  138. package/data/build/dist/host/cli-adapters/session-store.d.ts +7 -0
  139. package/data/build/dist/host/cli-adapters/session-store.js +39 -1
  140. package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +3 -0
  141. package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts +3 -0
  142. package/data/build/dist/host/cli-adapters/stream-normalizer.js +22 -2
  143. package/data/build/dist/host/control-api-client.d.ts +3 -3
  144. package/data/build/dist/host/control-api-client.js +2 -1
  145. package/data/build/dist/host/credential-store.js +11 -1
  146. package/data/build/dist/host/dsh-device-runtime.d.ts +4 -0
  147. package/data/build/dist/host/dsh-device-runtime.js +20 -9
  148. package/data/build/dist/host/features/auth.js +8 -5
  149. package/data/build/dist/host/features/debug.js +1 -1
  150. package/data/build/dist/host/features/git-management.d.ts +5 -0
  151. package/data/build/dist/host/features/git-management.js +290 -0
  152. package/data/build/dist/host/features/host-status.js +1 -1
  153. package/data/build/dist/host/features/index.d.ts +1 -0
  154. package/data/build/dist/host/features/index.js +3 -0
  155. package/data/build/dist/host/features/terminal-process.js +1 -1
  156. package/data/build/dist/host/features/terminal-status.d.ts +3 -1
  157. package/data/build/dist/host/features/terminal-status.js +3 -1
  158. package/data/build/dist/host/index.d.ts +7 -3
  159. package/data/build/dist/host/index.js +76 -11
  160. package/data/build/dist/host/lan-access-dsh.js +18 -5
  161. package/data/build/dist/host/native-session-bridge.d.ts +23 -1
  162. package/data/build/dist/host/native-session-bridge.js +157 -10
  163. package/data/build/dist/host/rpc-table.d.ts +1 -1
  164. package/data/build/dist/host/rpc.js +60 -10
  165. package/data/build/dist/host/session-migration-repair.d.ts +19 -0
  166. package/data/build/dist/host/session-migration-repair.js +395 -0
  167. package/data/build/dist/host/settings.d.ts +8 -1
  168. package/data/build/dist/host/settings.js +94 -3
  169. package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +1 -0
  170. package/data/build/dist/host/terminal/backends/tmux-backend.js +4 -2
  171. package/data/build/dist/host/terminal/controller-factory.d.ts +4 -0
  172. package/data/build/dist/host/terminal/controller-factory.js +26 -7
  173. package/data/build/dist/host/terminal/runtime-manager.d.ts +2 -1
  174. package/data/build/dist/host/terminal/runtime-manager.js +3 -0
  175. package/data/build/dist/host/terminal/startup.d.ts +2 -0
  176. package/data/build/dist/host/terminal/startup.js +2 -1
  177. package/data/build/dist/host/terminal/terminal-controller.d.ts +10 -3
  178. package/data/build/dist/host/terminal/terminal-controller.js +101 -15
  179. package/data/build/dist/host/terminal/terminal-process-service.js +3 -1
  180. package/data/build/dist/host/terminal/terminal-service.d.ts +2 -0
  181. package/data/build/dist/host/terminal/terminal-service.js +10 -0
  182. package/data/build/dist/index.d.ts +6 -2
  183. package/data/build/dist/index.js +8 -2
  184. package/data/build/dist/shared/contracts/cli-adapter.d.ts +14 -0
  185. package/data/build/dist/shared/contracts/config.d.ts +31 -1
  186. package/data/build/dist/shared/contracts/config.js +25 -0
  187. package/data/build/dist/shared/contracts/dsh-device.d.ts +13 -1
  188. package/data/build/dist/shared/contracts/errors.d.ts +1 -1
  189. package/data/build/dist/shared/contracts/feature.d.ts +6 -0
  190. package/data/build/dist/shared/contracts/git.d.ts +82 -0
  191. package/data/build/dist/shared/contracts/git.js +3 -0
  192. package/data/build/dist/shared/contracts/subscription.d.ts +46 -0
  193. package/data/build/dist/shared/contracts/terminal.d.ts +4 -1
  194. package/data/build/dist/shared/contracts/version.d.ts +2 -2
  195. package/data/build/dist/shared/contracts/version.js +4 -6
  196. package/data/build/dist/shared/debug.d.ts +14 -0
  197. package/data/build/dist/shared/debug.js +53 -0
  198. package/data/build/dist/shared/index.d.ts +7 -4
  199. package/data/build/dist/shared/index.js +2 -1
  200. package/data/build/dist/transport/debug.d.ts +1 -1
  201. package/data/build/dist/transport/debug.js +3 -30
  202. package/data/build/dist/transport/dsh-session.js +3 -3
  203. package/data/build/dist/typert.host.js +4 -2
  204. package/data/build/dist/typert.remote-client.d.ts +11 -0
  205. package/data/build/dist/typert.remote-client.js +7 -0
  206. package/dsh.bundle.patch +1 -1
  207. package/package.json +17 -5
  208. package/profile/README.md +4 -4
  209. package/profile/package.json +6 -4
  210. package/profile/scripts/check-dsh-install.mjs +2 -2
  211. package/profile/version.json +2 -2
  212. package/scripts/check-dsh-install.mjs +2 -2
  213. package/version.json +2 -2
@@ -1,4 +1,5 @@
1
1
  import { Service } from '@deepseek-ai/cordis';
2
+ import { debugInfo, debugWarn } from '../../shared/debug.js';
2
3
  class ObservableValue {
3
4
  value;
4
5
  listeners = new Set();
@@ -19,12 +20,12 @@ class ObservableValue {
19
20
  /** 一个 Sidebar 标签对应的终端模型;DOM 卸载只 detach,显式 close 才结束 Host 进程。 */
20
21
  export class CodingNsTerminalView {
21
22
  sessionId;
22
- id;
23
23
  remote;
24
24
  createWhenMissing;
25
25
  shellPath;
26
26
  onWorkspaceResolved;
27
27
  state;
28
+ id;
28
29
  store = new ObservableValue({ phase: 'idle', title: '终端', writable: false });
29
30
  lifetime = new AbortController();
30
31
  followController;
@@ -38,11 +39,11 @@ export class CodingNsTerminalView {
38
39
  lastResize;
39
40
  constructor(sessionId, id, remote, createWhenMissing, shellPath, onWorkspaceResolved) {
40
41
  this.sessionId = sessionId;
41
- this.id = id;
42
42
  this.remote = remote;
43
43
  this.createWhenMissing = createWhenMissing;
44
44
  this.shellPath = shellPath;
45
45
  this.onWorkspaceResolved = onWorkspaceResolved;
46
+ this.id = id;
46
47
  this.state = this.store;
47
48
  }
48
49
  mount() {
@@ -122,9 +123,11 @@ export class CodingNsTerminalView {
122
123
  async close() {
123
124
  if (this.store.getSnapshot().phase === 'closed')
124
125
  return;
126
+ debugInfo('codingns4dsh: client terminal close entered', { sessionId: this.sessionId, terminalId: this.id });
125
127
  this.patch({ phase: 'closing', writable: false });
126
128
  this.detach();
127
129
  unwrap(await resolveRemote(this.remote).close(this.sessionId, this.id));
130
+ debugInfo('codingns4dsh: client terminal close success', { sessionId: this.sessionId, terminalId: this.id });
128
131
  this.patch({ phase: 'closed', writable: false });
129
132
  }
130
133
  async dispose() {
@@ -137,11 +140,22 @@ export class CodingNsTerminalView {
137
140
  try {
138
141
  const remote = resolveRemote(this.remote);
139
142
  const environment = unwrap(await remote.environment(this.sessionId, this.lifetime.signal));
140
- if (environment.workspaceId !== undefined)
141
- this.onWorkspaceResolved?.(environment.workspaceId);
143
+ debugInfo('codingns4dsh: client terminal environment', { sessionId: this.sessionId, workspaceId: environment.workspaceId, cwd: environment.cwd });
144
+ let createWhenMissing = this.createWhenMissing;
145
+ if (environment.workspaceId !== undefined) {
146
+ const binding = this.onWorkspaceResolved?.(environment.workspaceId, this.id);
147
+ debugInfo('codingns4dsh: client terminal workspace binding', { sessionId: this.sessionId, workspaceId: environment.workspaceId, requestedId: this.id, binding: binding ?? null });
148
+ if (binding !== undefined) {
149
+ if (binding.id !== this.id)
150
+ this.id = binding.id;
151
+ // 工作区已有绑定时,恢复必须失败闭合,不能用新 ID 偷建替代终端。
152
+ createWhenMissing = !binding.existing;
153
+ }
154
+ }
142
155
  const listed = unwrap(await remote.list(this.sessionId));
156
+ debugInfo('codingns4dsh: client terminal list', { sessionId: this.sessionId, workspaceId: environment.workspaceId, terminalIds: listed.map((entry) => entry.id), requestedId: this.id, createWhenMissing });
143
157
  let info = listed.find((entry) => entry.id === this.id);
144
- if (info === undefined && this.createWhenMissing) {
158
+ if (info === undefined && createWhenMissing) {
145
159
  this.patch({ phase: 'creating', environment });
146
160
  info = unwrap(await remote.create(this.sessionId, {
147
161
  id: this.id,
@@ -149,6 +163,7 @@ export class CodingNsTerminalView {
149
163
  cols: Math.min(80, environment.maxCols),
150
164
  rows: Math.min(24, environment.maxRows),
151
165
  }, this.lifetime.signal));
166
+ debugInfo('codingns4dsh: client terminal created', { sessionId: this.sessionId, workspaceId: environment.workspaceId, terminalId: info.id });
152
167
  }
153
168
  if (info === undefined)
154
169
  throw new Error('Host 中不存在该终端,且恢复流程禁止自动创建替代进程');
@@ -238,14 +253,17 @@ export class CodingNsWebTerminals extends Service {
238
253
  recoveries = new Map();
239
254
  closeFailureStore = new ObservableValue([]);
240
255
  closeFailures = this.closeFailureStore;
256
+ remoteReadyStore = new ObservableValue(false);
257
+ remoteReadyState = this.remoteReadyStore;
241
258
  closeRequests = new Map();
259
+ /** Remote 注入前不能执行关闭请求;就绪后统一冲刷,避免把启动竞态显示成永久错误。 */
260
+ cleanupQueued = false;
242
261
  constructor(ctx, remote) {
243
262
  super(ctx, 'webTerminals');
244
263
  this.remote = remote;
245
264
  for (const request of readCloseRequests())
246
265
  this.closeRequests.set(String(request.id), request);
247
- for (const request of this.closeRequests.values())
248
- void this.cleanup(request);
266
+ void this.flushCleanup();
249
267
  }
250
268
  view(sessionId, key, contentId, terminalId, shellPath) {
251
269
  const mapKey = JSON.stringify([sessionId, key]);
@@ -254,19 +272,20 @@ export class CodingNsWebTerminals extends Service {
254
272
  return existing.view;
255
273
  const workspaceId = this.workspaceIds.get(sessionId);
256
274
  const saved = terminalId
257
- ?? (workspaceId === undefined ? undefined : readWorkspaceBinding(workspaceId, contentId))
275
+ ?? (workspaceId === undefined ? undefined : readWorkspaceBindingWithMigration(workspaceId, contentId))
258
276
  ?? readBinding(sessionId, contentId);
259
277
  const id = saved ?? crypto.randomUUID();
278
+ debugInfo('codingns4dsh: client terminal view', { sessionId, key, contentId, workspaceId: workspaceId ?? null, savedId: saved ?? null, terminalId: id, bindingSource: terminalId !== undefined ? 'argument' : workspaceId !== undefined && readWorkspaceBindingWithMigration(workspaceId, contentId) !== undefined ? 'workspace-storage' : readBinding(sessionId, contentId) !== undefined ? 'session-storage' : 'new' });
260
279
  if (saved === undefined)
261
280
  writeBinding(sessionId, contentId, id);
262
- const view = new CodingNsTerminalView(sessionId, id, this.remote, saved === undefined, shellPath, (resolvedWorkspaceId) => this.rememberWorkspace(sessionId, contentId, id, resolvedWorkspaceId));
281
+ const view = new CodingNsTerminalView(sessionId, id, this.remote, saved === undefined, shellPath, (resolvedWorkspaceId, currentId) => this.rememberWorkspace(sessionId, contentId, currentId, resolvedWorkspaceId));
263
282
  this.views.set(mapKey, { contentId, view });
264
283
  return view;
265
284
  }
266
285
  /** 返回某个 Sidebar 内容已经保存的 Host 终端身份,用于恢复时去重。 */
267
286
  boundTerminalId(sessionId, contentId) {
268
287
  const workspaceId = this.workspaceIds.get(sessionId);
269
- return (workspaceId === undefined ? undefined : readWorkspaceBinding(workspaceId, contentId))
288
+ return (workspaceId === undefined ? undefined : readWorkspaceBindingWithMigration(workspaceId, contentId))
270
289
  ?? readBinding(sessionId, contentId);
271
290
  }
272
291
  async launchShells(sessionId, signal) {
@@ -284,13 +303,16 @@ export class CodingNsWebTerminals extends Service {
284
303
  const id = terminalId ?? record?.view.id ?? this.boundTerminalId(sessionId, contentId);
285
304
  if (id === undefined)
286
305
  return;
306
+ debugInfo('codingns4dsh: client terminal close request', { sessionId, key, contentId, workspaceId: this.workspaceIds.get(sessionId) ?? null, terminalId: id, hasView: record !== undefined });
287
307
  const request = { sessionId, id, title: record?.view.state.getSnapshot().title ?? '终端' };
288
308
  this.closeRequests.set(String(id), request);
289
309
  persistCloseRequests(this.closeRequests.values());
290
310
  deleteBinding(sessionId, contentId);
291
311
  const workspaceId = this.workspaceIds.get(sessionId);
292
- if (workspaceId !== undefined)
293
- deleteWorkspaceBinding(workspaceId, contentId);
312
+ if (workspaceId !== undefined) {
313
+ deleteWorkspaceBinding(workspaceId);
314
+ deleteLegacyWorkspaceBinding(workspaceId, contentId);
315
+ }
294
316
  this.views.delete(mapKey);
295
317
  void this.cleanup(request, record?.view);
296
318
  }
@@ -303,7 +325,9 @@ export class CodingNsWebTerminals extends Service {
303
325
  const environment = unwrap(await resolveRemote(this.remote).environment(sessionId));
304
326
  if (environment.workspaceId !== undefined)
305
327
  this.workspaceIds.set(sessionId, environment.workspaceId);
306
- return unwrap(await resolveRemote(this.remote).list(sessionId));
328
+ const result = unwrap(await resolveRemote(this.remote).list(sessionId));
329
+ debugInfo('codingns4dsh: client terminal recover', { sessionId, workspaceId: environment.workspaceId, terminalIds: result.map((entry) => entry.id) });
330
+ return result;
307
331
  })().finally(() => this.recoveries.delete(sessionId));
308
332
  this.recoveries.set(sessionId, recovery);
309
333
  return recovery;
@@ -313,6 +337,12 @@ export class CodingNsWebTerminals extends Service {
313
337
  if (request !== undefined)
314
338
  void this.cleanup(request);
315
339
  }
340
+ /** 由 Client 的 Remote 注入回调调用,完成启动阶段延迟的关闭请求。 */
341
+ remoteReady() {
342
+ debugInfo('codingns4dsh: client terminal remote ready notification');
343
+ this.remoteReadyStore.set(true);
344
+ void this.flushCleanup();
345
+ }
316
346
  async dispose() {
317
347
  await Promise.all([...this.views.values()].map((record) => record.view.dispose()));
318
348
  this.views.clear();
@@ -321,23 +351,59 @@ export class CodingNsWebTerminals extends Service {
321
351
  }
322
352
  rememberWorkspace(sessionId, contentId, id, workspaceId) {
323
353
  this.workspaceIds.set(sessionId, workspaceId);
324
- writeWorkspaceBinding(workspaceId, contentId, id);
354
+ const existing = readWorkspaceBindingWithMigration(workspaceId, contentId);
355
+ const resolvedId = existing ?? id;
356
+ // 修正首次渲染时已经写入的会话键,避免第二个会话继续携带临时 ID。
357
+ writeBinding(sessionId, contentId, resolvedId);
358
+ // 旧版本的工作区键带 contentId;统一重写成只含 Workspace ID 的新键。
359
+ writeWorkspaceBinding(workspaceId, contentId, resolvedId);
360
+ deleteLegacyWorkspaceBinding(workspaceId, contentId);
361
+ return { id: resolvedId, existing: existing !== undefined };
325
362
  }
326
363
  async cleanup(request, view) {
327
364
  try {
365
+ debugInfo('codingns4dsh: client terminal cleanup begin', { sessionId: request.sessionId, terminalId: request.id, hasView: view !== undefined });
328
366
  if (view === undefined)
329
367
  unwrap(await resolveRemote(this.remote).close(request.sessionId, request.id));
330
368
  else
331
369
  await view.close();
370
+ debugInfo('codingns4dsh: client terminal cleanup success', { sessionId: request.sessionId, terminalId: request.id });
332
371
  this.closeRequests.delete(String(request.id));
333
372
  persistCloseRequests(this.closeRequests.values());
334
373
  this.closeFailureStore.set(this.closeFailureStore.getSnapshot().filter((item) => item.id !== request.id));
335
374
  }
336
375
  catch (error) {
376
+ if (isTerminalRemoteUnavailable(error)) {
377
+ debugInfo('codingns4dsh: client terminal cleanup deferred', { sessionId: request.sessionId, terminalId: request.id });
378
+ return;
379
+ }
380
+ debugWarn('codingns4dsh: client terminal cleanup failed', { sessionId: request.sessionId, terminalId: request.id, error: errorMessage(error) });
337
381
  const failure = { ...request, message: errorMessage(error) };
338
382
  this.closeFailureStore.set([...this.closeFailureStore.getSnapshot().filter((item) => item.id !== request.id), failure]);
339
383
  }
340
384
  }
385
+ async flushCleanup() {
386
+ if (this.cleanupQueued || this.closeRequests.size === 0)
387
+ return;
388
+ try {
389
+ resolveRemote(this.remote);
390
+ }
391
+ catch (error) {
392
+ if (isTerminalRemoteUnavailable(error)) {
393
+ debugInfo('codingns4dsh: client terminal cleanup waiting for remote');
394
+ return;
395
+ }
396
+ throw error;
397
+ }
398
+ this.cleanupQueued = true;
399
+ try {
400
+ for (const request of [...this.closeRequests.values()])
401
+ await this.cleanup(request);
402
+ }
403
+ finally {
404
+ this.cleanupQueued = false;
405
+ }
406
+ }
341
407
  }
342
408
  function unwrap(result) {
343
409
  if (typeof result !== 'object' || result === null || !('ok' in result)) {
@@ -359,10 +425,19 @@ function sameResize(left, right) {
359
425
  function errorMessage(error) {
360
426
  return error instanceof Error ? error.message : String(error);
361
427
  }
428
+ function isTerminalRemoteUnavailable(error) {
429
+ return errorMessage(error) === '终端服务尚未就绪,请稍后重试';
430
+ }
362
431
  function bindingKey(sessionId, contentId) {
363
432
  return `${BINDING_PREFIX}${JSON.stringify([sessionId, contentId])}`;
364
433
  }
365
- function workspaceBindingKey(workspaceId, contentId) {
434
+ function workspaceBindingKey(workspaceId) {
435
+ // 工作区模式的终端身份不能带 session、tab 或 contentId;这些值在每个
436
+ // DSH 会话中都会重新生成,带进去就会把所谓的工作区绑定重新拆回会话绑定。
437
+ return `${BINDING_PREFIX}${JSON.stringify(['workspace', workspaceId])}`;
438
+ }
439
+ /** 0.1.7 早期构建把 contentId 写进了工作区键,读取它只用于一次迁移。 */
440
+ function legacyWorkspaceBindingKey(workspaceId, contentId) {
366
441
  return `${BINDING_PREFIX}${JSON.stringify(['workspace', workspaceId, contentId])}`;
367
442
  }
368
443
  function readBinding(sessionId, contentId) {
@@ -372,16 +447,29 @@ function readBinding(sessionId, contentId) {
372
447
  function writeBinding(sessionId, contentId, id) {
373
448
  writeString(bindingKey(sessionId, contentId), String(id));
374
449
  }
375
- function readWorkspaceBinding(workspaceId, contentId) {
376
- const value = readString(workspaceBindingKey(workspaceId, contentId));
450
+ function readWorkspaceBinding(workspaceId, _contentId) {
451
+ const value = readString(workspaceBindingKey(workspaceId));
377
452
  return value !== null && /^[\w-]{1,128}$/u.test(value) ? value : undefined;
378
453
  }
379
- function writeWorkspaceBinding(workspaceId, contentId, id) {
380
- writeString(workspaceBindingKey(workspaceId, contentId), String(id));
454
+ function readWorkspaceBindingWithMigration(workspaceId, contentId) {
455
+ const current = readWorkspaceBinding(workspaceId);
456
+ if (current !== undefined)
457
+ return current;
458
+ const legacy = readString(legacyWorkspaceBindingKey(workspaceId, contentId));
459
+ return legacy !== null && /^[\w-]{1,128}$/u.test(legacy) ? legacy : undefined;
460
+ }
461
+ function writeWorkspaceBinding(workspaceId, _contentId, id) {
462
+ writeString(workspaceBindingKey(workspaceId), String(id));
463
+ }
464
+ function deleteWorkspaceBinding(workspaceId, _contentId) {
465
+ try {
466
+ localStorage.removeItem(workspaceBindingKey(workspaceId));
467
+ }
468
+ catch { /* 浏览器禁用存储时仅失去跨刷新绑定。 */ }
381
469
  }
382
- function deleteWorkspaceBinding(workspaceId, contentId) {
470
+ function deleteLegacyWorkspaceBinding(workspaceId, contentId) {
383
471
  try {
384
- localStorage.removeItem(workspaceBindingKey(workspaceId, contentId));
472
+ localStorage.removeItem(legacyWorkspaceBindingKey(workspaceId, contentId));
385
473
  }
386
474
  catch { /* 浏览器禁用存储时仅失去跨刷新绑定。 */ }
387
475
  }
@@ -0,0 +1,50 @@
1
+ import type { CodingNsWebTerminals, WebTerminalInfo } from './model.js';
2
+ /** Sidebar 记录的最小结构,避免恢复逻辑绑定某个 DSH 版本的类型包。 */
3
+ export interface TerminalSidebarTab {
4
+ readonly id: string;
5
+ readonly kind: string;
6
+ }
7
+ interface TerminalNavigationSnapshot {
8
+ readonly params?: unknown;
9
+ }
10
+ interface TerminalOccurrence {
11
+ readonly navigation: {
12
+ getSnapshot(): TerminalNavigationSnapshot;
13
+ };
14
+ }
15
+ export interface TerminalSidebarMountedSource {
16
+ readonly getSnapshot: () => string | undefined;
17
+ readonly subscribe: (listener: () => void) => () => void;
18
+ }
19
+ export interface TerminalSidebarRecoveryPort {
20
+ readonly tabsIn?: (sessionId: string) => readonly TerminalSidebarTab[];
21
+ readonly openTabs?: {
22
+ getSnapshot(): readonly {
23
+ readonly sessionId: string;
24
+ readonly tabId: string;
25
+ readonly kind: string;
26
+ }[];
27
+ subscribe?: (listener: () => void) => () => void;
28
+ };
29
+ readonly mounted?: TerminalSidebarMountedSource;
30
+ readonly openTabIn?: (sessionId: string, kind: string, options?: {
31
+ readonly params?: unknown;
32
+ }) => void;
33
+ readonly tabDomain?: {
34
+ occurrence(sessionId: string, tab: {
35
+ readonly id: string;
36
+ }): TerminalOccurrence;
37
+ };
38
+ }
39
+ export interface TerminalSessionRecovery {
40
+ ensure(sessionId: string): Promise<readonly WebTerminalInfo[]>;
41
+ }
42
+ /**
43
+ * 将 Host 已存在的终端补回当前 DSH 会话的 Sidebar。
44
+ *
45
+ * DSH 的布局和标签身份以 sessionId 为存储边界,所以工作区共享只解决
46
+ * Host 终端身份,不能替新会话创建 Sidebar 标签。这里把两个生命周期接起来。
47
+ */
48
+ export declare function createTerminalSessionRecovery(webTerminals: Pick<CodingNsWebTerminals, 'recover'>, sidebar: TerminalSidebarRecoveryPort, terminalKind: string): TerminalSessionRecovery;
49
+ export {};
50
+ //# sourceMappingURL=recovery.d.ts.map
@@ -0,0 +1,101 @@
1
+ import { debugInfo, debugWarn } from '../../shared/debug.js';
2
+ /**
3
+ * 将 Host 已存在的终端补回当前 DSH 会话的 Sidebar。
4
+ *
5
+ * DSH 的布局和标签身份以 sessionId 为存储边界,所以工作区共享只解决
6
+ * Host 终端身份,不能替新会话创建 Sidebar 标签。这里把两个生命周期接起来。
7
+ */
8
+ export function createTerminalSessionRecovery(webTerminals, sidebar, terminalKind) {
9
+ const pending = new Map();
10
+ const ensure = (sessionId) => {
11
+ const normalized = sessionId.trim();
12
+ if (normalized === '')
13
+ return Promise.resolve([]);
14
+ const current = pending.get(normalized);
15
+ if (current !== undefined)
16
+ return current;
17
+ debugInfo('codingns4dsh: client terminal sidebar recovery begin', { sessionId: normalized });
18
+ const task = recoverAndOpen(normalized)
19
+ .catch((cause) => {
20
+ debugWarn('codingns4dsh: client terminal sidebar recovery failed', { sessionId: normalized, error: messageOf(cause) });
21
+ throw cause;
22
+ })
23
+ .finally(() => pending.delete(normalized));
24
+ pending.set(normalized, task);
25
+ return task;
26
+ };
27
+ const recoverAndOpen = async (sessionId) => {
28
+ const terminals = await webTerminals.recover(sessionId);
29
+ const existing = readTabs(sidebar, sessionId);
30
+ debugInfo('codingns4dsh: client terminal sidebar recovery listed', {
31
+ sessionId,
32
+ terminalIds: terminals.map((terminal) => terminal.id),
33
+ existingTabs: existing.map((tab) => ({ id: tab.id, kind: tab.kind })),
34
+ });
35
+ const opened = new Set();
36
+ for (const tab of existing) {
37
+ if (tab.kind !== terminalKind)
38
+ continue;
39
+ const id = terminalIdOf(sidebar, sessionId, tab);
40
+ if (id === undefined) {
41
+ // 旧版标签没有导航参数时仍代表一个有效终端,避免恢复时重复创建。
42
+ for (const terminal of terminals)
43
+ opened.add(terminal.id);
44
+ debugInfo('codingns4dsh: client terminal sidebar recovery legacy tab', { sessionId, tabId: tab.id });
45
+ }
46
+ else {
47
+ opened.add(id);
48
+ }
49
+ }
50
+ for (const terminal of terminals) {
51
+ if (opened.has(terminal.id)) {
52
+ debugInfo('codingns4dsh: client terminal sidebar recovery skip existing', { sessionId, terminalId: terminal.id });
53
+ continue;
54
+ }
55
+ if (typeof sidebar.openTabIn !== 'function') {
56
+ debugWarn('codingns4dsh: client terminal sidebar recovery unavailable', { sessionId, terminalId: terminal.id, reason: 'openTabIn-undefined' });
57
+ continue;
58
+ }
59
+ sidebar.openTabIn(sessionId, terminalKind, { params: { terminalId: terminal.id } });
60
+ debugInfo('codingns4dsh: client terminal sidebar recovery opened', { sessionId, terminalId: terminal.id });
61
+ opened.add(terminal.id);
62
+ }
63
+ return terminals;
64
+ };
65
+ return { ensure };
66
+ }
67
+ function readTabs(sidebar, sessionId) {
68
+ if (typeof sidebar.tabsIn === 'function') {
69
+ try {
70
+ return sidebar.tabsIn(sessionId);
71
+ }
72
+ catch {
73
+ return [];
74
+ }
75
+ }
76
+ return readOpenTabs(sidebar)
77
+ .filter((tab) => tab.sessionId === sessionId)
78
+ .map((tab) => ({ id: tab.tabId, kind: tab.kind }));
79
+ }
80
+ function readOpenTabs(sidebar) {
81
+ try {
82
+ return sidebar.openTabs?.getSnapshot() ?? [];
83
+ }
84
+ catch {
85
+ return [];
86
+ }
87
+ }
88
+ function terminalIdOf(sidebar, sessionId, tab) {
89
+ try {
90
+ const params = sidebar.tabDomain?.occurrence(sessionId, tab).navigation.getSnapshot().params;
91
+ if (typeof params !== 'object' || params === null)
92
+ return undefined;
93
+ const terminalId = params.terminalId;
94
+ return typeof terminalId === 'string' && terminalId.trim() !== '' ? terminalId : undefined;
95
+ }
96
+ catch {
97
+ return undefined;
98
+ }
99
+ }
100
+ function messageOf(value) { return value instanceof Error ? value.message : String(value); }
101
+ //# sourceMappingURL=recovery.js.map
@@ -1,7 +1,7 @@
1
1
  import type { Context } from '@deepseek-ai/cordis';
2
- import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
3
2
  import type { CodingNsSettings } from '../../shared/contracts/config.js';
4
3
  import { CodingNsWebTerminals, type WebTerminalId } from './model.js';
4
+ import type { CodingNsSettingsStore } from '../../dsh-capabilities/settings-store.js';
5
5
  export declare const TERMINAL_PROVIDER_ID = "codingns4dsh/terminal";
6
6
  export declare const TERMINAL_KIND = "terminal";
7
7
  interface TerminalParams {
@@ -9,7 +9,7 @@ interface TerminalParams {
9
9
  readonly shellPath?: string;
10
10
  }
11
11
  /** 注册终端类型及其所有公开 Sidebar Slot。 */
12
- export declare function registerCodingNsTerminalUi(ctx: Context, webTerminals: CodingNsWebTerminals, settings: SettingsScope<CodingNsSettings>): () => void;
12
+ export declare function registerCodingNsTerminalUi(ctx: Context, webTerminals: CodingNsWebTerminals, settings: CodingNsSettingsStore<CodingNsSettings>): () => void;
13
13
  declare module '@deepseek-ai/dsh-client-ui-sidebar-right/client' {
14
14
  interface SidebarRightTabParamsMap {
15
15
  terminal: TerminalParams;
@@ -1,6 +1,8 @@
1
1
  import { createElement, Fragment, useEffect, useLayoutEffect, useRef, useState, useSyncExternalStore, } from 'react';
2
2
  import { Button, IconChevronDownOutline14, Menu, } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { resolveChevronDownIcon } from '../../dsh-capabilities/client/primitives-adapter.js';
3
4
  import { CodingNsWebTerminals } from './model.js';
5
+ import { createTerminalSessionRecovery } from './recovery.js';
4
6
  import { installTerminalStyles, terminalClass } from './styles.js';
5
7
  import { CodingNsXtermView } from './xterm-view.js';
6
8
  import { codingNsTranslator, useCodingNsTranslator } from '../locale.js';
@@ -18,8 +20,9 @@ export function registerCodingNsTerminalUi(ctx, webTerminals, settings) {
18
20
  const registerCloseHandler = sidebarRight.registerCloseHandler;
19
21
  // alpha2 提供多 Tab 元数据和关闭 API;rc3 没有这些字段,不能直接调用。
20
22
  const legacyCloseFallback = typeof registerCloseHandler !== 'function';
21
- const supportsMultiple = typeof ctx.sidebarRight.openTabs !== 'undefined';
22
- const guideEntrySlot = ctx.slots.specDynamic?.('sidebar.right.tab.guide.entry') !== undefined;
23
+ const recoverySidebar = sidebarRight;
24
+ const supportsMultiple = readSidebarOpenTabs(recoverySidebar) !== undefined;
25
+ const recovery = createTerminalSessionRecovery(webTerminals, recoverySidebar, TERMINAL_KIND);
23
26
  disposers.push(installTerminalStyles());
24
27
  const tabDefinition = {
25
28
  id: TERMINAL_PROVIDER_ID,
@@ -44,14 +47,13 @@ export function registerCodingNsTerminalUi(ctx, webTerminals, settings) {
44
47
  name: 'sidebar.right.pane.tab.title', key: TERMINAL_PROVIDER_ID,
45
48
  inject: () => ({ webTerminals, locale: ctx.locale }),
46
49
  }, TerminalTitle)));
47
- if (guideEntrySlot)
48
- disposers.push(ctx.slots.inject('sidebar.right.tab.guide.entry', () => ctx.slots.register({
49
- name: 'sidebar.right.tab.guide.entry', key: TERMINAL_PROVIDER_ID,
50
- inject: () => ({ webTerminals, locale: ctx.locale }),
51
- }, TerminalGuide)));
50
+ disposers.push(ctx.slots.inject('sidebar.right.tab.guide.entry', () => ctx.slots.register({
51
+ name: 'sidebar.right.tab.guide.entry', key: TERMINAL_PROVIDER_ID,
52
+ inject: () => ({ webTerminals, recoverSession: recovery.ensure, locale: ctx.locale }),
53
+ }, TerminalGuide)));
52
54
  disposers.push(ctx.slots.inject('shell.overlay', () => ctx.slots.register({
53
55
  name: 'shell.overlay', id: 'codingns4dsh-terminal-cleanup', order: 1000,
54
- inject: () => ({ webTerminals, locale: ctx.locale }),
56
+ inject: () => ({ webTerminals, locale: ctx.locale, sidebarRight: recoverySidebar, recoverSession: recovery.ensure }),
55
57
  }, TerminalCleanup)));
56
58
  if (typeof registerCloseHandler === 'function')
57
59
  disposers.push(registerCloseHandler.call(ctx.sidebarRight, TERMINAL_KIND, (sessionId, tab) => {
@@ -141,12 +143,13 @@ function TerminalTitle({ sessionId, useTabInfo, webTerminals, locale }) {
141
143
  title: t('terminal.rename'),
142
144
  }, state.title));
143
145
  }
144
- function TerminalGuide({ sessionId, useTabInfo, webTerminals, title, description, locale }) {
146
+ function TerminalGuide({ sessionId, useTabInfo, webTerminals, recoverSession, title, description, locale }) {
145
147
  const t = useCodingNsTranslator(locale);
146
148
  const info = useTabInfo();
147
149
  const [open, setOpen] = useState(false);
148
150
  const [attempt, setAttempt] = useState(0);
149
151
  const [state, setState] = useState({ phase: 'loading' });
152
+ useEffect(() => { void recoverSession(String(sessionId)).catch(() => undefined); }, [recoverSession, sessionId]);
150
153
  useEffect(() => {
151
154
  if (!open)
152
155
  return;
@@ -200,7 +203,7 @@ function TerminalGuide({ sessionId, useTabInfo, webTerminals, title, description
200
203
  'aria-haspopup': 'menu',
201
204
  'aria-expanded': open,
202
205
  onClick: () => { setState({ phase: 'loading' }); setOpen((value) => !value); },
203
- }, createElement(IconChevronDownOutline14)),
206
+ }, createElement(resolveChevronDownIcon())),
204
207
  }));
205
208
  }
206
209
  function shellMenuItems(state, t) {
@@ -215,8 +218,23 @@ function shellMenuItems(state, t) {
215
218
  return [{ id: 'empty', label: t('terminal.noShell'), disabled: true }];
216
219
  return state.shells.map((shell) => ({ id: shell.path, label: shell.name }));
217
220
  }
218
- function TerminalCleanup({ webTerminals, locale }) {
221
+ function TerminalCleanup({ webTerminals, locale, sidebarRight, recoverSession }) {
219
222
  const t = useCodingNsTranslator(locale);
223
+ const openTabs = readSidebarOpenTabs(sidebarRight);
224
+ const openTabSnapshot = useSyncExternalStore(openTabs?.subscribe ?? noSubscribe, openTabs?.getSnapshot ?? noOpenTabs, openTabs?.getSnapshot ?? noOpenTabs);
225
+ const remoteReady = useSyncExternalStore(webTerminals.remoteReadyState.subscribe.bind(webTerminals.remoteReadyState), webTerminals.remoteReadyState.getSnapshot.bind(webTerminals.remoteReadyState), webTerminals.remoteReadyState.getSnapshot.bind(webTerminals.remoteReadyState));
226
+ const mounted = readSidebarMounted(sidebarRight);
227
+ const mountedSessionId = useSyncExternalStore(mounted?.subscribe ?? noSubscribe, mounted?.getSnapshot ?? noMountedSession, mounted?.getSnapshot ?? noMountedSession);
228
+ useEffect(() => {
229
+ // 0.1.7 才提供 mounted 会话 observable;旧版本保留原有 Guide 挂载路径。
230
+ if (!remoteReady || mounted === undefined)
231
+ return;
232
+ const sessionIds = new Set(openTabSnapshot.map((tab) => String(tab.sessionId).trim()).filter(Boolean));
233
+ if (mountedSessionId !== undefined)
234
+ sessionIds.add(String(mountedSessionId).trim());
235
+ for (const sessionId of sessionIds)
236
+ void recoverSession(sessionId).catch(() => undefined);
237
+ }, [openTabSnapshot, recoverSession, remoteReady, mountedSessionId]);
220
238
  const failures = useSyncExternalStore(webTerminals.closeFailures.subscribe.bind(webTerminals.closeFailures), webTerminals.closeFailures.getSnapshot.bind(webTerminals.closeFailures));
221
239
  if (failures.length === 0)
222
240
  return null;
@@ -226,6 +244,26 @@ function TerminalCleanup({ webTerminals, locale }) {
226
244
  role: 'alert',
227
245
  }, createElement('span', undefined, t('terminal.cleanupFailed', { title: failure.title, message: failure.message })), createElement('button', { type: 'button', onClick: () => webTerminals.retryClose(failure.id) }, t('terminal.retry')))));
228
246
  }
247
+ function readSidebarOpenTabs(sidebarRight) {
248
+ try {
249
+ return sidebarRight.openTabs;
250
+ }
251
+ catch {
252
+ return undefined;
253
+ }
254
+ }
255
+ function readSidebarMounted(sidebarRight) {
256
+ try {
257
+ return sidebarRight.mounted;
258
+ }
259
+ catch {
260
+ return undefined;
261
+ }
262
+ }
263
+ function noMountedSession() { return undefined; }
264
+ const EMPTY_OPEN_TABS = [];
265
+ function noOpenTabs() { return EMPTY_OPEN_TABS; }
266
+ function noSubscribe() { return () => undefined; }
229
267
  function terminalParams(info) {
230
268
  const params = info.tab.navigation.params;
231
269
  return typeof params === 'object' && params !== null ? params : {};
@@ -1,10 +1,10 @@
1
1
  import type { ReactElement } from 'react';
2
- import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
2
+ import type { CodingNsSettingsStore } from '../../dsh-capabilities/settings-store.js';
3
3
  import { type CodingNsSettings } from '../../shared/contracts/config.js';
4
4
  import type { CodingNsTerminalView } from './model.js';
5
5
  export interface CodingNsXtermViewProps {
6
6
  readonly view: CodingNsTerminalView;
7
- readonly settings: SettingsScope<CodingNsSettings>;
7
+ readonly settings: CodingNsSettingsStore<CodingNsSettings>;
8
8
  readonly themeRevision: number;
9
9
  readonly onNewTerminal: () => void;
10
10
  }
@@ -3,6 +3,7 @@ import { Button, IconPlusOutline16 } from '@deepseek-ai/dsh-client-ui-primitives
3
3
  import { FitAddon } from '@xterm/addon-fit';
4
4
  import { Terminal } from '@xterm/xterm';
5
5
  import xtermCss from '@xterm/xterm/css/xterm.css';
6
+ import { resolvePlusIcon } from '../../dsh-capabilities/client/primitives-adapter.js';
6
7
  import { DEFAULT_TERMINAL_ENHANCEMENT_SETTINGS, } from '../../shared/contracts/config.js';
7
8
  import { terminalClass } from './styles.js';
8
9
  /** 使用与 DSH 内置终端相同的布局、状态条和 xterm 默认参数。 */
@@ -116,7 +117,7 @@ function TerminalStatus({ state, view, onNewTerminal, }) {
116
117
  }, state.phase === 'disconnected' ? '重新连接' : '重试') : null, ended ? createElement(Button, {
117
118
  variant: 'primary',
118
119
  size: 'sm',
119
- icon: createElement(IconPlusOutline16),
120
+ icon: createElement(resolvePlusIcon()),
120
121
  onClick: onNewTerminal,
121
122
  }, '新建终端') : null);
122
123
  }
@@ -14,7 +14,7 @@ export declare const dshThemeColor: {
14
14
  readonly inputBackground: "var(--dsw-specific-input-major, Canvas)";
15
15
  readonly surfaceSubtle: "var(--dsw-alias-bg-secondary, rgba(127, 127, 127, 0.06))";
16
16
  readonly buttonBackground: "var(--dsw-alias-button-elevated-fill, transparent)";
17
- readonly menuBackground: "var(--dsw-specific-menu, Canvas)";
17
+ readonly menuBackground: "var(--dsw-alias-bg-layer-3, var(--dsw-alias-bg-l1, var(--dsw-specific-menu, Canvas)))";
18
18
  readonly pageBackground: "var(--dsw-alias-bg-primary, Canvas)";
19
19
  readonly overlay: "var(--dsw-alias-bg-mask-1, rgba(0, 0, 0, 0.45))";
20
20
  readonly accent: "var(--dsw-alias-button-info-fill, #1677ff)";
@@ -51,6 +51,11 @@ export declare const dshSettingsRowStyle: CSSProperties;
51
51
  export declare const dshSettingsListRowStyle: CSSProperties;
52
52
  export declare const dshSettingsHelpStyle: CSSProperties;
53
53
  export declare const dshSettingsNoteStyle: CSSProperties;
54
+ /** 终端外观设置的紧凑双列布局。 */
55
+ export declare const dshSettingsGridStyle: CSSProperties;
56
+ export declare const dshSettingsSectionHeaderStyle: CSSProperties;
57
+ /** 设置写入结果的顶部浮动提示。 */
58
+ export declare const dshSettingsToastStyle: CSSProperties;
54
59
  /** 模态框和弹出菜单必须成对设置前景色与背景色。 */
55
60
  export declare const dshPopupSurfaceStyle: CSSProperties;
56
61
  //# sourceMappingURL=theme.d.ts.map