@jingyi0605/codingns4dsh 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1114 -0
- package/assets/provider-icons/claude-code.png +0 -0
- package/assets/provider-icons/codex.png +0 -0
- package/assets/provider-icons/command-code.svg +5 -0
- package/assets/provider-icons/deepseek-harness.svg +9 -0
- package/assets/provider-icons/gemini.png +0 -0
- package/assets/provider-icons/grok.png +0 -0
- package/assets/provider-icons/kimi.png +0 -0
- package/assets/provider-icons/opencode.png +0 -0
- package/assets/provider-icons/pi.svg +7 -0
- package/data/build/dist/bootstrap/dsh-connection-adapter.d.ts +97 -0
- package/data/build/dist/bootstrap/dsh-connection-adapter.js +169 -0
- package/data/build/dist/bootstrap/index.d.ts +38 -0
- package/data/build/dist/bootstrap/index.js +44 -0
- package/data/build/dist/client/account-bar.d.ts +9 -0
- package/data/build/dist/client/account-bar.js +398 -0
- package/data/build/dist/client/bundle.js +26167 -0
- package/data/build/dist/client/cli-catalog.d.ts +24 -0
- package/data/build/dist/client/cli-catalog.js +108 -0
- package/data/build/dist/client/cli-slots.d.ts +38 -0
- package/data/build/dist/client/cli-slots.js +297 -0
- package/data/build/dist/client/debug/ui.d.ts +12 -0
- package/data/build/dist/client/debug/ui.js +451 -0
- package/data/build/dist/client/dsh-h5-bootstrap.d.ts +64 -0
- package/data/build/dist/client/dsh-h5-bootstrap.js +293 -0
- package/data/build/dist/client/dsh-runtime-version.d.ts +3 -0
- package/data/build/dist/client/dsh-runtime-version.js +11 -0
- package/data/build/dist/client/external-tool-stream.d.ts +24 -0
- package/data/build/dist/client/external-tool-stream.js +114 -0
- package/data/build/dist/client/features/cli-adapters.d.ts +7 -0
- package/data/build/dist/client/features/cli-adapters.js +215 -0
- package/data/build/dist/client/features/debug.d.ts +4 -0
- package/data/build/dist/client/features/debug.js +24 -0
- package/data/build/dist/client/features/index.d.ts +33 -0
- package/data/build/dist/client/features/index.js +44 -0
- package/data/build/dist/client/features/lan-access-panel.d.ts +5 -0
- package/data/build/dist/client/features/lan-access-panel.js +146 -0
- package/data/build/dist/client/features/lan-access.d.ts +9 -0
- package/data/build/dist/client/features/lan-access.js +30 -0
- package/data/build/dist/client/features/login-protection-panel.d.ts +5 -0
- package/data/build/dist/client/features/login-protection-panel.js +91 -0
- package/data/build/dist/client/features/login-protection-session.d.ts +6 -0
- package/data/build/dist/client/features/login-protection-session.js +25 -0
- package/data/build/dist/client/features/login-protection.d.ts +4 -0
- package/data/build/dist/client/features/login-protection.js +23 -0
- package/data/build/dist/client/features/reverse-proxy-panel.d.ts +9 -0
- package/data/build/dist/client/features/reverse-proxy-panel.js +159 -0
- package/data/build/dist/client/features/reverse-proxy.d.ts +12 -0
- package/data/build/dist/client/features/reverse-proxy.js +112 -0
- package/data/build/dist/client/features/terminal-enhancement-panel.d.ts +5 -0
- package/data/build/dist/client/features/terminal-enhancement-panel.js +160 -0
- package/data/build/dist/client/features/terminal-enhancement.d.ts +4 -0
- package/data/build/dist/client/features/terminal-enhancement.js +24 -0
- package/data/build/dist/client/features/types.d.ts +58 -0
- package/data/build/dist/client/features/types.js +2 -0
- package/data/build/dist/client/features/workspace-session-enhancement-panel.d.ts +5 -0
- package/data/build/dist/client/features/workspace-session-enhancement-panel.js +66 -0
- package/data/build/dist/client/features/workspace-session-enhancement.d.ts +4 -0
- package/data/build/dist/client/features/workspace-session-enhancement.js +101 -0
- package/data/build/dist/client/h5-bootstrap-entry.d.ts +2 -0
- package/data/build/dist/client/h5-bootstrap-entry.js +17 -0
- package/data/build/dist/client/index.d.ts +31 -0
- package/data/build/dist/client/index.js +98 -0
- package/data/build/dist/client/lan-access.d.ts +28 -0
- package/data/build/dist/client/lan-access.js +68 -0
- package/data/build/dist/client/locale.d.ts +20 -0
- package/data/build/dist/client/locale.js +393 -0
- package/data/build/dist/client/provider-icon-assets.d.ts +2 -0
- package/data/build/dist/client/provider-icon-assets.js +23 -0
- package/data/build/dist/client/provider-icons.d.ts +19 -0
- package/data/build/dist/client/provider-icons.js +39 -0
- package/data/build/dist/client/remote-web-context.d.ts +57 -0
- package/data/build/dist/client/remote-web-context.js +939 -0
- package/data/build/dist/client/session-adapter-cache.d.ts +18 -0
- package/data/build/dist/client/session-adapter-cache.js +84 -0
- package/data/build/dist/client/settings-bridge.d.ts +32 -0
- package/data/build/dist/client/settings-bridge.js +126 -0
- package/data/build/dist/client/settings-section.d.ts +30 -0
- package/data/build/dist/client/settings-section.js +93 -0
- package/data/build/dist/client/subscription-slot.d.ts +18 -0
- package/data/build/dist/client/subscription-slot.js +223 -0
- package/data/build/dist/client/terminal/index.d.ts +4 -0
- package/data/build/dist/client/terminal/index.js +3 -0
- package/data/build/dist/client/terminal/model.d.ts +116 -0
- package/data/build/dist/client/terminal/model.js +435 -0
- package/data/build/dist/client/terminal/styles.d.ts +21 -0
- package/data/build/dist/client/terminal/styles.js +51 -0
- package/data/build/dist/client/terminal/ui.d.ts +19 -0
- package/data/build/dist/client/terminal/ui.js +257 -0
- package/data/build/dist/client/terminal/xterm-view.d.ts +13 -0
- package/data/build/dist/client/terminal/xterm-view.js +213 -0
- package/data/build/dist/client/theme.d.ts +56 -0
- package/data/build/dist/client/theme.js +186 -0
- package/data/build/dist/client/workspace-session-archive-dom.d.ts +29 -0
- package/data/build/dist/client/workspace-session-archive-dom.js +525 -0
- package/data/build/dist/client/workspace-session-fiber.d.ts +3 -0
- package/data/build/dist/client/workspace-session-fiber.js +33 -0
- package/data/build/dist/client/workspace-session-logo-dom.d.ts +26 -0
- package/data/build/dist/client/workspace-session-logo-dom.js +128 -0
- package/data/build/dist/features/index.d.ts +3 -0
- package/data/build/dist/features/index.js +2 -0
- package/data/build/dist/features/registry.d.ts +74 -0
- package/data/build/dist/features/registry.js +382 -0
- package/data/build/dist/features/resource-scope/index.d.ts +39 -0
- package/data/build/dist/features/resource-scope/index.js +161 -0
- package/data/build/dist/host/auth-session.d.ts +42 -0
- package/data/build/dist/host/auth-session.js +186 -0
- package/data/build/dist/host/cli-adapters/claude-driver.d.ts +14 -0
- package/data/build/dist/host/cli-adapters/claude-driver.js +103 -0
- package/data/build/dist/host/cli-adapters/codex-driver.d.ts +45 -0
- package/data/build/dist/host/cli-adapters/codex-driver.js +534 -0
- package/data/build/dist/host/cli-adapters/command-code-driver.d.ts +38 -0
- package/data/build/dist/host/cli-adapters/command-code-driver.js +394 -0
- package/data/build/dist/host/cli-adapters/command-code-subscription.d.ts +19 -0
- package/data/build/dist/host/cli-adapters/command-code-subscription.js +126 -0
- package/data/build/dist/host/cli-adapters/driver.d.ts +41 -0
- package/data/build/dist/host/cli-adapters/driver.js +2 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.d.ts +37 -0
- package/data/build/dist/host/cli-adapters/dsh-message-projector.js +180 -0
- package/data/build/dist/host/cli-adapters/dsh-tool-history.d.ts +48 -0
- package/data/build/dist/host/cli-adapters/dsh-tool-history.js +373 -0
- package/data/build/dist/host/cli-adapters/feature.d.ts +7 -0
- package/data/build/dist/host/cli-adapters/feature.js +335 -0
- package/data/build/dist/host/cli-adapters/gemini-driver.d.ts +24 -0
- package/data/build/dist/host/cli-adapters/gemini-driver.js +427 -0
- package/data/build/dist/host/cli-adapters/grok-driver.d.ts +38 -0
- package/data/build/dist/host/cli-adapters/grok-driver.js +372 -0
- package/data/build/dist/host/cli-adapters/http-sse-client.d.ts +22 -0
- package/data/build/dist/host/cli-adapters/http-sse-client.js +99 -0
- package/data/build/dist/host/cli-adapters/interaction-events.d.ts +11 -0
- package/data/build/dist/host/cli-adapters/interaction-events.js +80 -0
- package/data/build/dist/host/cli-adapters/json-rpc-process.d.ts +65 -0
- package/data/build/dist/host/cli-adapters/json-rpc-process.js +256 -0
- package/data/build/dist/host/cli-adapters/kimi-driver.d.ts +21 -0
- package/data/build/dist/host/cli-adapters/kimi-driver.js +382 -0
- package/data/build/dist/host/cli-adapters/model-catalog.d.ts +22 -0
- package/data/build/dist/host/cli-adapters/model-catalog.js +89 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.d.ts +55 -0
- package/data/build/dist/host/cli-adapters/opencode-driver.js +728 -0
- package/data/build/dist/host/cli-adapters/pi-driver.d.ts +43 -0
- package/data/build/dist/host/cli-adapters/pi-driver.js +312 -0
- package/data/build/dist/host/cli-adapters/process-utils.d.ts +16 -0
- package/data/build/dist/host/cli-adapters/process-utils.js +27 -0
- package/data/build/dist/host/cli-adapters/provider-subscription.d.ts +82 -0
- package/data/build/dist/host/cli-adapters/provider-subscription.js +610 -0
- package/data/build/dist/host/cli-adapters/registry.d.ts +95 -0
- package/data/build/dist/host/cli-adapters/registry.js +659 -0
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.d.ts +24 -0
- package/data/build/dist/host/cli-adapters/rpc-driver-utils.js +119 -0
- package/data/build/dist/host/cli-adapters/session-probe.d.ts +20 -0
- package/data/build/dist/host/cli-adapters/session-probe.js +139 -0
- package/data/build/dist/host/cli-adapters/session-store.d.ts +68 -0
- package/data/build/dist/host/cli-adapters/session-store.js +180 -0
- package/data/build/dist/host/cli-adapters/standard-stream-driver.d.ts +45 -0
- package/data/build/dist/host/cli-adapters/standard-stream-driver.js +205 -0
- package/data/build/dist/host/cli-adapters/stream-normalizer.d.ts +25 -0
- package/data/build/dist/host/cli-adapters/stream-normalizer.js +83 -0
- package/data/build/dist/host/cli-adapters/tool-observation.d.ts +9 -0
- package/data/build/dist/host/cli-adapters/tool-observation.js +39 -0
- package/data/build/dist/host/control-api-client.d.ts +84 -0
- package/data/build/dist/host/control-api-client.js +130 -0
- package/data/build/dist/host/credential-store.d.ts +54 -0
- package/data/build/dist/host/credential-store.js +114 -0
- package/data/build/dist/host/debug.d.ts +79 -0
- package/data/build/dist/host/debug.js +295 -0
- package/data/build/dist/host/dsh-device-runtime.d.ts +39 -0
- package/data/build/dist/host/dsh-device-runtime.js +118 -0
- package/data/build/dist/host/dsh-gateway-feature.d.ts +5 -0
- package/data/build/dist/host/dsh-gateway-feature.js +55 -0
- package/data/build/dist/host/dsh-runtime-version.d.ts +12 -0
- package/data/build/dist/host/dsh-runtime-version.js +69 -0
- package/data/build/dist/host/features/auth.d.ts +11 -0
- package/data/build/dist/host/features/auth.js +273 -0
- package/data/build/dist/host/features/debug.d.ts +5 -0
- package/data/build/dist/host/features/debug.js +90 -0
- package/data/build/dist/host/features/host-status.d.ts +12 -0
- package/data/build/dist/host/features/host-status.js +54 -0
- package/data/build/dist/host/features/index.d.ts +19 -0
- package/data/build/dist/host/features/index.js +29 -0
- package/data/build/dist/host/features/lan-access-dsh.d.ts +8 -0
- package/data/build/dist/host/features/lan-access-dsh.js +45 -0
- package/data/build/dist/host/features/terminal-process.d.ts +5 -0
- package/data/build/dist/host/features/terminal-process.js +110 -0
- package/data/build/dist/host/features/terminal-status.d.ts +18 -0
- package/data/build/dist/host/features/terminal-status.js +56 -0
- package/data/build/dist/host/features/types.d.ts +41 -0
- package/data/build/dist/host/features/types.js +2 -0
- package/data/build/dist/host/index.d.ts +43 -0
- package/data/build/dist/host/index.js +135 -0
- package/data/build/dist/host/lan-access-dsh.d.ts +83 -0
- package/data/build/dist/host/lan-access-dsh.js +831 -0
- package/data/build/dist/host/native-session-bridge.d.ts +130 -0
- package/data/build/dist/host/native-session-bridge.js +317 -0
- package/data/build/dist/host/relay-tunnel-runtime.d.ts +97 -0
- package/data/build/dist/host/relay-tunnel-runtime.js +361 -0
- package/data/build/dist/host/remote-web-runtime.d.ts +78 -0
- package/data/build/dist/host/remote-web-runtime.js +559 -0
- package/data/build/dist/host/rpc-table.d.ts +32 -0
- package/data/build/dist/host/rpc-table.js +47 -0
- package/data/build/dist/host/rpc.d.ts +18 -0
- package/data/build/dist/host/rpc.js +248 -0
- package/data/build/dist/host/settings.d.ts +19 -0
- package/data/build/dist/host/settings.js +80 -0
- package/data/build/dist/host/terminal/attachment-registry.d.ts +17 -0
- package/data/build/dist/host/terminal/attachment-registry.js +49 -0
- package/data/build/dist/host/terminal/backends/conpty-backend.d.ts +39 -0
- package/data/build/dist/host/terminal/backends/conpty-backend.js +160 -0
- package/data/build/dist/host/terminal/backends/local-pty-backend.d.ts +36 -0
- package/data/build/dist/host/terminal/backends/local-pty-backend.js +178 -0
- package/data/build/dist/host/terminal/backends/tmux-backend.d.ts +40 -0
- package/data/build/dist/host/terminal/backends/tmux-backend.js +191 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-client.d.ts +27 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-client.js +154 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-process.d.ts +10 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-process.js +147 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-protocol.d.ts +92 -0
- package/data/build/dist/host/terminal/broker/conpty-broker-protocol.js +134 -0
- package/data/build/dist/host/terminal/controller-factory.d.ts +37 -0
- package/data/build/dist/host/terminal/controller-factory.js +82 -0
- package/data/build/dist/host/terminal/index.d.ts +17 -0
- package/data/build/dist/host/terminal/index.js +17 -0
- package/data/build/dist/host/terminal/runtime-adapter.d.ts +77 -0
- package/data/build/dist/host/terminal/runtime-adapter.js +18 -0
- package/data/build/dist/host/terminal/runtime-manager.d.ts +41 -0
- package/data/build/dist/host/terminal/runtime-manager.js +179 -0
- package/data/build/dist/host/terminal/shell-detection.d.ts +23 -0
- package/data/build/dist/host/terminal/shell-detection.js +105 -0
- package/data/build/dist/host/terminal/startup.d.ts +33 -0
- package/data/build/dist/host/terminal/startup.js +100 -0
- package/data/build/dist/host/terminal/terminal-controller.d.ts +78 -0
- package/data/build/dist/host/terminal/terminal-controller.js +324 -0
- package/data/build/dist/host/terminal/terminal-process-service.d.ts +30 -0
- package/data/build/dist/host/terminal/terminal-process-service.js +194 -0
- package/data/build/dist/host/terminal/terminal-process-store.d.ts +45 -0
- package/data/build/dist/host/terminal/terminal-process-store.js +183 -0
- package/data/build/dist/host/terminal/terminal-service.d.ts +87 -0
- package/data/build/dist/host/terminal/terminal-service.js +528 -0
- package/data/build/dist/host/terminal/terminal-store.d.ts +49 -0
- package/data/build/dist/host/terminal/terminal-store.js +242 -0
- package/data/build/dist/index.d.ts +37 -0
- package/data/build/dist/index.js +26 -0
- package/data/build/dist/shared/contracts/auth.d.ts +122 -0
- package/data/build/dist/shared/contracts/auth.js +8 -0
- package/data/build/dist/shared/contracts/cli-adapter.d.ts +192 -0
- package/data/build/dist/shared/contracts/cli-adapter.js +2 -0
- package/data/build/dist/shared/contracts/command-code.d.ts +5 -0
- package/data/build/dist/shared/contracts/command-code.js +2 -0
- package/data/build/dist/shared/contracts/config.d.ts +113 -0
- package/data/build/dist/shared/contracts/config.js +86 -0
- package/data/build/dist/shared/contracts/debug-config.d.ts +28 -0
- package/data/build/dist/shared/contracts/debug-config.js +78 -0
- package/data/build/dist/shared/contracts/debug.d.ts +75 -0
- package/data/build/dist/shared/contracts/debug.js +121 -0
- package/data/build/dist/shared/contracts/dsh-device.d.ts +72 -0
- package/data/build/dist/shared/contracts/dsh-device.js +2 -0
- package/data/build/dist/shared/contracts/errors.d.ts +33 -0
- package/data/build/dist/shared/contracts/errors.js +41 -0
- package/data/build/dist/shared/contracts/feature.d.ts +71 -0
- package/data/build/dist/shared/contracts/feature.js +2 -0
- package/data/build/dist/shared/contracts/host-status.d.ts +14 -0
- package/data/build/dist/shared/contracts/host-status.js +2 -0
- package/data/build/dist/shared/contracts/lan-access-dsh.d.ts +30 -0
- package/data/build/dist/shared/contracts/lan-access-dsh.js +2 -0
- package/data/build/dist/shared/contracts/peer-host.d.ts +37 -0
- package/data/build/dist/shared/contracts/peer-host.js +9 -0
- package/data/build/dist/shared/contracts/signaling.d.ts +67 -0
- package/data/build/dist/shared/contracts/signaling.js +2 -0
- package/data/build/dist/shared/contracts/subscription.d.ts +69 -0
- package/data/build/dist/shared/contracts/subscription.js +2 -0
- package/data/build/dist/shared/contracts/terminal-process.d.ts +65 -0
- package/data/build/dist/shared/contracts/terminal-process.js +2 -0
- package/data/build/dist/shared/contracts/terminal.d.ts +127 -0
- package/data/build/dist/shared/contracts/terminal.js +2 -0
- package/data/build/dist/shared/contracts/transport.d.ts +45 -0
- package/data/build/dist/shared/contracts/transport.js +3 -0
- package/data/build/dist/shared/contracts/version.d.ts +19 -0
- package/data/build/dist/shared/contracts/version.js +74 -0
- package/data/build/dist/shared/index.d.ts +22 -0
- package/data/build/dist/shared/index.js +9 -0
- package/data/build/dist/transport/carrier.d.ts +38 -0
- package/data/build/dist/transport/carrier.js +268 -0
- package/data/build/dist/transport/debug.d.ts +22 -0
- package/data/build/dist/transport/debug.js +57 -0
- package/data/build/dist/transport/dsh-envelope.d.ts +41 -0
- package/data/build/dist/transport/dsh-envelope.js +107 -0
- package/data/build/dist/transport/dsh-gateway.d.ts +64 -0
- package/data/build/dist/transport/dsh-gateway.js +254 -0
- package/data/build/dist/transport/dsh-session.d.ts +55 -0
- package/data/build/dist/transport/dsh-session.js +256 -0
- package/data/build/dist/transport/dsh-transport.d.ts +57 -0
- package/data/build/dist/transport/dsh-transport.js +125 -0
- package/data/build/dist/transport/frame.d.ts +132 -0
- package/data/build/dist/transport/frame.js +168 -0
- package/data/build/dist/transport/index.d.ts +11 -0
- package/data/build/dist/transport/index.js +11 -0
- package/data/build/dist/transport/multiplexer.d.ts +63 -0
- package/data/build/dist/transport/multiplexer.js +323 -0
- package/data/build/dist/transport/webrtc-client.d.ts +65 -0
- package/data/build/dist/transport/webrtc-client.js +255 -0
- package/data/build/dist/transport/webrtc-host.d.ts +58 -0
- package/data/build/dist/transport/webrtc-host.js +252 -0
- package/data/build/dist/typert.host.d.ts +25 -0
- package/data/build/dist/typert.host.js +150 -0
- package/dsh.bundle.patch +12 -0
- package/package.json +135 -0
- package/profile/README.md +27 -0
- package/profile/cordis.patch.yml +2 -0
- package/profile/package.json +20 -0
- package/profile/scripts/check-dsh-install.mjs +71 -0
- package/profile/version.json +5 -0
- package/scripts/check-dsh-install.mjs +88 -0
- package/version.json +6 -0
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
/** 归档入口和模态框节点使用的标记,便于重复扫描与停用时完整清理。 */
|
|
2
|
+
export const WORKSPACE_SESSION_ARCHIVE_ATTRIBUTE = 'data-codingns-session-archive';
|
|
3
|
+
export const WORKSPACE_SESSION_ARCHIVE_MODAL_ATTRIBUTE = 'data-codingns-session-archive-modal';
|
|
4
|
+
const MORE_SESSION_PATTERN = /(?:展开|显示|expand|show).*(?:其余|更多|remaining|more).*(?:会话|sessions?)/iu;
|
|
5
|
+
/** 读取当前 DSH Workspace 的归档会话摘要,独立导出供契约测试和宿主探测使用。 */
|
|
6
|
+
export async function loadWorkspaceArchivedSessions(remote, now = Date.now) {
|
|
7
|
+
return (await loadArchiveSnapshot(normalizeRemote(remote), now)).byWorkspace;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 在原生工作区会话列表的“展开更多会话”按钮前插入归档入口。
|
|
11
|
+
*
|
|
12
|
+
* DSH 0.1.6 的侧栏没有可供插件追加内容的 Slot,因此这里只读 DOM 和 Fiber
|
|
13
|
+
* 身份;真正的归档和取消归档仍交给 DSH Workspace Controller,避免旁路修改存储。
|
|
14
|
+
*/
|
|
15
|
+
export function startWorkspaceSessionArchiveDom(options = {}) {
|
|
16
|
+
const dom = options.document ?? (typeof document === 'undefined' ? undefined : document);
|
|
17
|
+
const Observer = options.MutationObserver
|
|
18
|
+
?? (typeof MutationObserver === 'undefined' ? undefined : MutationObserver);
|
|
19
|
+
const remote = normalizeRemote(options.remote);
|
|
20
|
+
const now = options.now ?? Date.now;
|
|
21
|
+
let disposed = false;
|
|
22
|
+
let scanQueued = false;
|
|
23
|
+
let loading;
|
|
24
|
+
let snapshot = emptyArchiveSnapshot();
|
|
25
|
+
const refreshData = async () => {
|
|
26
|
+
if (disposed || remote.workspace?.follow === undefined || remote.session?.list === undefined)
|
|
27
|
+
return;
|
|
28
|
+
if (loading !== undefined) {
|
|
29
|
+
await loading;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
loading = loadArchiveSnapshot(remote, now).then((next) => {
|
|
33
|
+
if (disposed)
|
|
34
|
+
return;
|
|
35
|
+
snapshot = next;
|
|
36
|
+
scan();
|
|
37
|
+
}).catch(() => undefined).finally(() => {
|
|
38
|
+
loading = undefined;
|
|
39
|
+
});
|
|
40
|
+
await loading;
|
|
41
|
+
};
|
|
42
|
+
const scan = () => {
|
|
43
|
+
if (disposed || dom === undefined)
|
|
44
|
+
return;
|
|
45
|
+
// 扫描会移除并重建入口;暂时断开观察器,避免自身 DOM 变更触发无限重扫。
|
|
46
|
+
observer?.disconnect();
|
|
47
|
+
try {
|
|
48
|
+
removeArchiveEntries(dom);
|
|
49
|
+
const onlyWorkspaceId = snapshot.workspaceIds.length === 1
|
|
50
|
+
? snapshot.workspaceIds[0]
|
|
51
|
+
: undefined;
|
|
52
|
+
const buttons = findMoreSessionButtons(dom);
|
|
53
|
+
const useWorkspaceOrder = buttons.length === snapshot.workspaceIds.length;
|
|
54
|
+
const insertedWorkspaceIds = new Set();
|
|
55
|
+
const expandedByWorkspace = new Map(findWorkspaceHeaders(dom).flatMap((header) => {
|
|
56
|
+
const workspaceId = resolveWorkspaceId(header);
|
|
57
|
+
return workspaceId === undefined ? [] : [[workspaceId, header.getAttribute('aria-expanded') !== 'false']];
|
|
58
|
+
}));
|
|
59
|
+
for (const [index, button] of buttons.entries()) {
|
|
60
|
+
const workspaceId = resolveWorkspaceId(button)
|
|
61
|
+
?? onlyWorkspaceId
|
|
62
|
+
?? (useWorkspaceOrder ? snapshot.workspaceIds[index] : undefined);
|
|
63
|
+
if (workspaceId === undefined)
|
|
64
|
+
continue;
|
|
65
|
+
const items = snapshot.byWorkspace.get(workspaceId) ?? [];
|
|
66
|
+
if (insertArchiveEntry(button, items, dom, remote, workspaceId, expandedByWorkspace.get(workspaceId) ?? true, () => { void refreshData().then(() => openArchiveModal(snapshot.byWorkspace.get(workspaceId) ?? [], dom, remote, () => { void refreshData(); })); }))
|
|
67
|
+
insertedWorkspaceIds.add(workspaceId);
|
|
68
|
+
}
|
|
69
|
+
// 某些窗口高度下 DSH 不渲染“展开其余会话”按钮。归档入口仍必须
|
|
70
|
+
// 按工作区显示,此时放到该工作区会话列表的最底部。
|
|
71
|
+
for (const header of findWorkspaceHeaders(dom)) {
|
|
72
|
+
const workspaceId = resolveWorkspaceId(header);
|
|
73
|
+
if (workspaceId === undefined || insertedWorkspaceIds.has(workspaceId))
|
|
74
|
+
continue;
|
|
75
|
+
const items = snapshot.byWorkspace.get(workspaceId) ?? [];
|
|
76
|
+
if (insertArchiveEntryAfterHeader(header, items, dom, remote, workspaceId, header.getAttribute('aria-expanded') !== 'false', () => { void refreshData().then(() => openArchiveModal(snapshot.byWorkspace.get(workspaceId) ?? [], dom, remote, () => { void refreshData(); })); }))
|
|
77
|
+
insertedWorkspaceIds.add(workspaceId);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
if (!disposed && observer !== undefined && dom.documentElement !== null) {
|
|
82
|
+
observer.observe(dom.documentElement, {
|
|
83
|
+
childList: true,
|
|
84
|
+
subtree: true,
|
|
85
|
+
attributes: true,
|
|
86
|
+
attributeFilter: ['aria-expanded'],
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const scheduleScan = () => {
|
|
92
|
+
if (disposed || scanQueued)
|
|
93
|
+
return;
|
|
94
|
+
scanQueued = true;
|
|
95
|
+
queueMicrotask(() => {
|
|
96
|
+
scanQueued = false;
|
|
97
|
+
scan();
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
const observer = dom === undefined || Observer === undefined
|
|
101
|
+
? undefined
|
|
102
|
+
: new Observer(scheduleScan);
|
|
103
|
+
if (observer !== undefined && dom !== undefined) {
|
|
104
|
+
observer.observe(dom.documentElement, {
|
|
105
|
+
childList: true,
|
|
106
|
+
subtree: true,
|
|
107
|
+
attributes: true,
|
|
108
|
+
attributeFilter: ['aria-expanded'],
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
scan();
|
|
112
|
+
void refreshData();
|
|
113
|
+
return {
|
|
114
|
+
refresh() {
|
|
115
|
+
scheduleScan();
|
|
116
|
+
void refreshData();
|
|
117
|
+
},
|
|
118
|
+
dispose() {
|
|
119
|
+
if (disposed)
|
|
120
|
+
return;
|
|
121
|
+
disposed = true;
|
|
122
|
+
observer?.disconnect();
|
|
123
|
+
if (dom !== undefined) {
|
|
124
|
+
removeArchiveEntries(dom);
|
|
125
|
+
closeArchiveModal(dom);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function emptyArchiveSnapshot() {
|
|
131
|
+
return { byWorkspace: new Map(), workspaceIds: [] };
|
|
132
|
+
}
|
|
133
|
+
async function loadArchiveSnapshot(remote, now) {
|
|
134
|
+
const [workspaces, sessions] = await Promise.all([
|
|
135
|
+
readWorkspaceBaseline(remote.workspace),
|
|
136
|
+
readSessionList(remote.session),
|
|
137
|
+
]);
|
|
138
|
+
const byId = new Map(sessions.map((item) => [item.sessionId, item]));
|
|
139
|
+
const result = new Map();
|
|
140
|
+
for (const workspace of workspaces) {
|
|
141
|
+
const items = [];
|
|
142
|
+
for (const sessionId of workspace.archivedSessionIds) {
|
|
143
|
+
const session = byId.get(sessionId);
|
|
144
|
+
if (session === undefined || !belongsToWorkspace(session, workspace, workspaces))
|
|
145
|
+
continue;
|
|
146
|
+
items.push({
|
|
147
|
+
sessionId,
|
|
148
|
+
title: session.title,
|
|
149
|
+
archivedAt: session.archivedAt > 0 ? session.archivedAt : session.updatedAt > 0 ? session.updatedAt : now(),
|
|
150
|
+
...(workspace.workspaceId ? { workspaceId: workspace.workspaceId } : {}),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
items.sort((left, right) => right.archivedAt - left.archivedAt);
|
|
154
|
+
if (items.length > 0)
|
|
155
|
+
result.set(workspace.workspaceId, items);
|
|
156
|
+
}
|
|
157
|
+
return { byWorkspace: result, workspaceIds: workspaces.map((workspace) => workspace.workspaceId) };
|
|
158
|
+
}
|
|
159
|
+
async function readWorkspaceBaseline(api) {
|
|
160
|
+
if (api?.follow === undefined)
|
|
161
|
+
return [];
|
|
162
|
+
const source = await api.follow();
|
|
163
|
+
const iterator = source[Symbol.asyncIterator]();
|
|
164
|
+
const first = await iterator.next();
|
|
165
|
+
await iterator.return?.();
|
|
166
|
+
const frame = asRecord(first.value);
|
|
167
|
+
const value = asRecord(frame?.value);
|
|
168
|
+
const records = Array.isArray(value?.items) ? value.items : [];
|
|
169
|
+
const globalArchived = readStringArray(value?.archivedSessionIds);
|
|
170
|
+
return records.flatMap((item) => {
|
|
171
|
+
const record = asRecord(item);
|
|
172
|
+
const workspaceId = readString(record?.workspaceId);
|
|
173
|
+
if (workspaceId === undefined)
|
|
174
|
+
return [];
|
|
175
|
+
const path = readString(record?.path);
|
|
176
|
+
return [{
|
|
177
|
+
workspaceId,
|
|
178
|
+
...(path === undefined ? {} : { path }),
|
|
179
|
+
sessionIds: readStringArray(record?.sessionIds),
|
|
180
|
+
archivedSessionIds: globalArchived,
|
|
181
|
+
}];
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
async function readSessionList(api) {
|
|
185
|
+
if (api?.list === undefined)
|
|
186
|
+
return [];
|
|
187
|
+
const result = asRecord(unwrapRemoteValue(await api.list({})));
|
|
188
|
+
const items = Array.isArray(result?.items) ? result.items : [];
|
|
189
|
+
return items.flatMap((item) => {
|
|
190
|
+
const record = asRecord(item);
|
|
191
|
+
const sessionId = readString(record?.sessionId);
|
|
192
|
+
if (sessionId === undefined)
|
|
193
|
+
return [];
|
|
194
|
+
const projections = asRecord(record?.projections);
|
|
195
|
+
const values = asRecord(projections?.values);
|
|
196
|
+
const titleValue = values?.title;
|
|
197
|
+
const cwd = readString(record?.cwd);
|
|
198
|
+
const workspaceIdValue = readString(record?.workspaceId);
|
|
199
|
+
const archivedAt = typeof record?.archivedAt === 'number' && Number.isFinite(record.archivedAt) ? record.archivedAt : 0;
|
|
200
|
+
return [{
|
|
201
|
+
sessionId,
|
|
202
|
+
updatedAt: typeof record?.updatedAt === 'number' && Number.isFinite(record.updatedAt) ? record.updatedAt : 0,
|
|
203
|
+
archivedAt,
|
|
204
|
+
title: typeof titleValue === 'string' && titleValue.trim() ? titleValue.trim() : sessionId,
|
|
205
|
+
...(cwd === undefined ? {} : { cwd }),
|
|
206
|
+
...(workspaceIdValue === undefined ? {} : { workspaceId: workspaceIdValue }),
|
|
207
|
+
}];
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
function belongsToWorkspace(session, workspace, workspaces) {
|
|
211
|
+
if (workspace.sessionIds.includes(session.sessionId))
|
|
212
|
+
return true;
|
|
213
|
+
if (session.workspaceId !== undefined)
|
|
214
|
+
return session.workspaceId === workspace.workspaceId;
|
|
215
|
+
if (session.cwd !== undefined && workspace.path !== undefined)
|
|
216
|
+
return isPathWithin(session.cwd, workspace.path);
|
|
217
|
+
return workspaces.length === 1;
|
|
218
|
+
}
|
|
219
|
+
function isPathWithin(candidate, parent) {
|
|
220
|
+
const normalizedCandidate = candidate.replaceAll('\\', '/').replace(/\/+$/u, '');
|
|
221
|
+
const normalizedParent = parent.replaceAll('\\', '/').replace(/\/+$/u, '');
|
|
222
|
+
return normalizedCandidate === normalizedParent || normalizedCandidate.startsWith(`${normalizedParent}/`);
|
|
223
|
+
}
|
|
224
|
+
function findMoreSessionButtons(dom) {
|
|
225
|
+
return [...dom.querySelectorAll('button')].filter(isMoreSessionButton);
|
|
226
|
+
}
|
|
227
|
+
function findWorkspaceHeaders(dom) {
|
|
228
|
+
return [...dom.querySelectorAll('[role="treeitem"][aria-expanded]')];
|
|
229
|
+
}
|
|
230
|
+
function resolveWorkspaceId(element) {
|
|
231
|
+
let current = element;
|
|
232
|
+
for (let depth = 0; depth < 8 && current !== null; depth += 1) {
|
|
233
|
+
for (const key of ['data-workspace-id', 'data-workspaceid', 'data-workspace']) {
|
|
234
|
+
const value = current.getAttribute(key);
|
|
235
|
+
if (value?.trim())
|
|
236
|
+
return value.trim();
|
|
237
|
+
}
|
|
238
|
+
const fiberKey = Object.getOwnPropertyNames(current).find((key) => key.startsWith('__reactFiber$'));
|
|
239
|
+
const fiber = fiberKey === undefined ? undefined : current[fiberKey];
|
|
240
|
+
const id = findWorkspaceIdInFiber(fiber);
|
|
241
|
+
if (id !== undefined)
|
|
242
|
+
return id;
|
|
243
|
+
current = current.parentElement;
|
|
244
|
+
}
|
|
245
|
+
return undefined;
|
|
246
|
+
}
|
|
247
|
+
function findWorkspaceIdInFiber(value) {
|
|
248
|
+
let current = value;
|
|
249
|
+
for (let depth = 0; depth < 24 && isRecord(current); depth += 1) {
|
|
250
|
+
for (const props of [current.memoizedProps, current.pendingProps]) {
|
|
251
|
+
const id = findWorkspaceIdInProps(props);
|
|
252
|
+
if (id !== undefined)
|
|
253
|
+
return id;
|
|
254
|
+
}
|
|
255
|
+
current = current.return;
|
|
256
|
+
}
|
|
257
|
+
return undefined;
|
|
258
|
+
}
|
|
259
|
+
function findWorkspaceIdInProps(value) {
|
|
260
|
+
if (!isRecord(value))
|
|
261
|
+
return undefined;
|
|
262
|
+
for (const key of ['workspaceId', 'workspaceID']) {
|
|
263
|
+
const id = readString(value[key]);
|
|
264
|
+
if (id !== undefined)
|
|
265
|
+
return id;
|
|
266
|
+
}
|
|
267
|
+
for (const key of ['workspace', 'group', 'row', 'item', 'value', 'data']) {
|
|
268
|
+
const workspace = asRecord(value[key]);
|
|
269
|
+
const id = readString(workspace?.workspaceId) ?? readString(workspace?.workspaceID) ?? readString(workspace?.id);
|
|
270
|
+
if (id !== undefined)
|
|
271
|
+
return id;
|
|
272
|
+
}
|
|
273
|
+
return undefined;
|
|
274
|
+
}
|
|
275
|
+
function insertArchiveEntry(moreButton, items, dom, remote, workspaceId, expanded, onOpen) {
|
|
276
|
+
if (items.length === 0)
|
|
277
|
+
return false;
|
|
278
|
+
const container = findWorkspaceContainer(moreButton, workspaceId);
|
|
279
|
+
if (container === null)
|
|
280
|
+
return false;
|
|
281
|
+
const entry = createArchiveEntry(items, dom, expanded, onOpen);
|
|
282
|
+
const anchor = directChildFor(container, moreButton);
|
|
283
|
+
if (anchor === null)
|
|
284
|
+
return false;
|
|
285
|
+
container.insertBefore(entry, anchor);
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
function insertArchiveEntryAfterHeader(header, items, dom, remote, workspaceId, expanded, onOpen) {
|
|
289
|
+
if (items.length === 0)
|
|
290
|
+
return false;
|
|
291
|
+
const container = findWorkspaceContainer(header, workspaceId);
|
|
292
|
+
if (container === null)
|
|
293
|
+
return false;
|
|
294
|
+
const entry = createArchiveEntry(items, dom, expanded, onOpen);
|
|
295
|
+
const moreButton = [...container.querySelectorAll('button')].find(isMoreSessionButton);
|
|
296
|
+
if (moreButton !== undefined) {
|
|
297
|
+
const anchor = directChildFor(container, moreButton);
|
|
298
|
+
if (anchor !== null)
|
|
299
|
+
container.insertBefore(entry, anchor);
|
|
300
|
+
else
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
else
|
|
304
|
+
container.appendChild(entry);
|
|
305
|
+
return true;
|
|
306
|
+
}
|
|
307
|
+
function findWorkspaceContainer(anchor, workspaceId) {
|
|
308
|
+
let current = anchor.parentElement;
|
|
309
|
+
let fallback = null;
|
|
310
|
+
for (let depth = 0; depth < 8 && current !== null; depth += 1) {
|
|
311
|
+
fallback = current;
|
|
312
|
+
const hasMoreButton = [...current.querySelectorAll('button')].some((button) => {
|
|
313
|
+
return isMoreSessionButton(button) && (resolveWorkspaceId(button) === workspaceId || button === anchor);
|
|
314
|
+
});
|
|
315
|
+
const hasSessionRow = [...current.querySelectorAll('[role="treeitem"]')].some((row) => {
|
|
316
|
+
return row !== anchor && row.getAttribute('aria-expanded') === null;
|
|
317
|
+
});
|
|
318
|
+
if (hasMoreButton || hasSessionRow)
|
|
319
|
+
return current;
|
|
320
|
+
current = current.parentElement;
|
|
321
|
+
}
|
|
322
|
+
return fallback;
|
|
323
|
+
}
|
|
324
|
+
function directChildFor(container, descendant) {
|
|
325
|
+
let current = descendant;
|
|
326
|
+
while (current.parentElement !== null && current.parentElement !== container) {
|
|
327
|
+
current = current.parentElement;
|
|
328
|
+
}
|
|
329
|
+
return current.parentElement === container ? current : null;
|
|
330
|
+
}
|
|
331
|
+
function createArchiveEntry(items, dom, expanded, onOpen) {
|
|
332
|
+
const entry = dom.createElement('button');
|
|
333
|
+
entry.type = 'button';
|
|
334
|
+
entry.setAttribute(WORKSPACE_SESSION_ARCHIVE_ATTRIBUTE, '');
|
|
335
|
+
entry.setAttribute('aria-label', '已归档的会话');
|
|
336
|
+
entry.hidden = !expanded;
|
|
337
|
+
entry.setAttribute('aria-hidden', expanded ? 'false' : 'true');
|
|
338
|
+
entry.textContent = `已归档的会话 ${items.length}`;
|
|
339
|
+
Object.assign(entry.style, {
|
|
340
|
+
display: expanded ? 'block' : 'none',
|
|
341
|
+
width: '100%',
|
|
342
|
+
margin: '2px 0',
|
|
343
|
+
padding: '7px 12px 7px 42px',
|
|
344
|
+
border: '0',
|
|
345
|
+
color: 'var(--dsw-alias-label-secondary, GrayText)',
|
|
346
|
+
background: 'transparent',
|
|
347
|
+
textAlign: 'left',
|
|
348
|
+
cursor: 'pointer',
|
|
349
|
+
font: 'inherit',
|
|
350
|
+
});
|
|
351
|
+
entry.addEventListener('click', onOpen);
|
|
352
|
+
return entry;
|
|
353
|
+
}
|
|
354
|
+
function isMoreSessionButton(button) {
|
|
355
|
+
const label = `${button.textContent ?? ''} ${button.getAttribute('aria-label') ?? ''}`.replace(/\s+/gu, ' ');
|
|
356
|
+
if (MORE_SESSION_PATTERN.test(label))
|
|
357
|
+
return true;
|
|
358
|
+
// React 文本节点可能被拆分或带换行,按三个稳定语义片段兜底识别。
|
|
359
|
+
return /(?:展开|显示|expand|show)/iu.test(label)
|
|
360
|
+
&& /(?:其余|更多|remaining|more)/iu.test(label)
|
|
361
|
+
&& /(?:会话|sessions?)/iu.test(label);
|
|
362
|
+
}
|
|
363
|
+
function removeArchiveEntries(dom) {
|
|
364
|
+
for (const node of dom.querySelectorAll(`[${WORKSPACE_SESSION_ARCHIVE_ATTRIBUTE}]`))
|
|
365
|
+
node.remove();
|
|
366
|
+
}
|
|
367
|
+
function openArchiveModal(items, dom, remote, onChanged) {
|
|
368
|
+
closeArchiveModal(dom);
|
|
369
|
+
if (dom.body === null || dom.body === undefined)
|
|
370
|
+
return;
|
|
371
|
+
const overlay = dom.createElement('div');
|
|
372
|
+
overlay.setAttribute(WORKSPACE_SESSION_ARCHIVE_MODAL_ATTRIBUTE, '');
|
|
373
|
+
Object.assign(overlay.style, {
|
|
374
|
+
position: 'fixed', inset: '0', zIndex: '9999', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
375
|
+
padding: '24px', boxSizing: 'border-box', background: 'var(--dsw-alias-bg-mask-1, rgba(0,0,0,.45))',
|
|
376
|
+
});
|
|
377
|
+
const surface = dom.createElement('section');
|
|
378
|
+
surface.setAttribute('role', 'dialog');
|
|
379
|
+
surface.setAttribute('aria-modal', 'true');
|
|
380
|
+
surface.setAttribute('aria-label', '已归档的会话');
|
|
381
|
+
Object.assign(surface.style, {
|
|
382
|
+
width: 'min(860px, 100%)', maxHeight: 'min(720px, 90vh)', overflow: 'auto', boxSizing: 'border-box',
|
|
383
|
+
padding: '28px 32px', borderRadius: '16px', color: 'var(--dsw-alias-label-primary, CanvasText)',
|
|
384
|
+
background: 'var(--dsw-specific-menu, Canvas)', boxShadow: 'var(--dsw-elevation-prominent, 0 12px 40px rgba(0,0,0,.25))',
|
|
385
|
+
});
|
|
386
|
+
const header = dom.createElement('div');
|
|
387
|
+
Object.assign(header.style, { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '16px', marginBottom: '18px' });
|
|
388
|
+
const title = dom.createElement('h2');
|
|
389
|
+
title.textContent = '已归档的会话';
|
|
390
|
+
Object.assign(title.style, { margin: '0', fontSize: '22px', fontWeight: '600' });
|
|
391
|
+
const close = dom.createElement('button');
|
|
392
|
+
close.type = 'button';
|
|
393
|
+
close.setAttribute('aria-label', '关闭');
|
|
394
|
+
close.textContent = '×';
|
|
395
|
+
Object.assign(close.style, { border: '0', background: 'transparent', color: 'inherit', fontSize: '30px', lineHeight: '1', cursor: 'pointer' });
|
|
396
|
+
header.append(title, close);
|
|
397
|
+
const search = dom.createElement('input');
|
|
398
|
+
search.type = 'search';
|
|
399
|
+
search.placeholder = '搜索已归档会话';
|
|
400
|
+
search.setAttribute('aria-label', '搜索已归档会话');
|
|
401
|
+
Object.assign(search.style, { width: '100%', boxSizing: 'border-box', padding: '11px 14px', marginBottom: '16px', border: '1px solid var(--dsw-alias-border-l2, #d9d9d9)', borderRadius: '8px', color: 'inherit', background: 'var(--dsw-specific-input-major, Canvas)', font: 'inherit' });
|
|
402
|
+
const list = dom.createElement('div');
|
|
403
|
+
Object.assign(list.style, { display: 'flex', flexDirection: 'column', gap: '4px' });
|
|
404
|
+
const render = () => {
|
|
405
|
+
list.replaceChildren();
|
|
406
|
+
const keyword = search.value.trim().toLocaleLowerCase();
|
|
407
|
+
const filtered = items.filter((item) => item.title.toLocaleLowerCase().includes(keyword));
|
|
408
|
+
for (const item of filtered)
|
|
409
|
+
list.appendChild(createArchiveRow(item, dom, remote, onChanged));
|
|
410
|
+
if (filtered.length === 0) {
|
|
411
|
+
const empty = dom.createElement('p');
|
|
412
|
+
empty.textContent = '没有找到归档会话。';
|
|
413
|
+
Object.assign(empty.style, { margin: '20px 0', color: 'var(--dsw-alias-label-secondary, GrayText)', textAlign: 'center' });
|
|
414
|
+
list.appendChild(empty);
|
|
415
|
+
}
|
|
416
|
+
};
|
|
417
|
+
close.addEventListener('click', () => closeArchiveModal(dom));
|
|
418
|
+
overlay.addEventListener('click', (event) => { if (event.target === overlay)
|
|
419
|
+
closeArchiveModal(dom); });
|
|
420
|
+
overlay.addEventListener('keydown', (event) => {
|
|
421
|
+
if (event.key === 'Escape')
|
|
422
|
+
closeArchiveModal(dom);
|
|
423
|
+
});
|
|
424
|
+
search.addEventListener('input', render);
|
|
425
|
+
surface.append(header, search, list);
|
|
426
|
+
overlay.appendChild(surface);
|
|
427
|
+
dom.body.appendChild(overlay);
|
|
428
|
+
render();
|
|
429
|
+
search.focus();
|
|
430
|
+
}
|
|
431
|
+
function createArchiveRow(item, dom, remote, onChanged) {
|
|
432
|
+
const row = dom.createElement('div');
|
|
433
|
+
Object.assign(row.style, { display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '16px', padding: '12px 0', borderBottom: '1px solid var(--dsw-alias-border-l1, rgba(0,0,0,.08))' });
|
|
434
|
+
const content = dom.createElement('div');
|
|
435
|
+
Object.assign(content.style, { minWidth: '0', display: 'flex', flexDirection: 'column', gap: '4px' });
|
|
436
|
+
const name = dom.createElement('strong');
|
|
437
|
+
name.textContent = item.title;
|
|
438
|
+
name.title = item.title;
|
|
439
|
+
Object.assign(name.style, { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontSize: '15px' });
|
|
440
|
+
const time = dom.createElement('span');
|
|
441
|
+
time.textContent = `归档于 ${formatArchiveTime(item.archivedAt)}`;
|
|
442
|
+
Object.assign(time.style, { color: 'var(--dsw-alias-label-secondary, GrayText)', fontSize: '13px' });
|
|
443
|
+
content.append(name, time);
|
|
444
|
+
const restore = dom.createElement('button');
|
|
445
|
+
restore.type = 'button';
|
|
446
|
+
restore.textContent = '取消归档';
|
|
447
|
+
const unarchive = remote.workspace?.unarchiveSession;
|
|
448
|
+
const canUnarchive = typeof unarchive === 'function';
|
|
449
|
+
restore.disabled = !canUnarchive;
|
|
450
|
+
restore.title = canUnarchive ? '取消归档' : '当前 DSH 版本不支持取消归档';
|
|
451
|
+
if (!canUnarchive)
|
|
452
|
+
restore.textContent = '取消归档(当前版本不支持)';
|
|
453
|
+
Object.assign(restore.style, { flex: '0 0 auto', padding: '7px 12px', border: '1px solid var(--dsw-alias-border-l2, #d9d9d9)', borderRadius: '8px', color: 'inherit', background: 'transparent', cursor: canUnarchive ? 'pointer' : 'not-allowed', font: 'inherit', opacity: canUnarchive ? '1' : '0.55' });
|
|
454
|
+
restore.addEventListener('click', async () => {
|
|
455
|
+
if (!canUnarchive || unarchive === undefined)
|
|
456
|
+
return;
|
|
457
|
+
restore.disabled = true;
|
|
458
|
+
restore.textContent = '处理中…';
|
|
459
|
+
try {
|
|
460
|
+
await unarchive({ sessionId: item.sessionId });
|
|
461
|
+
row.remove();
|
|
462
|
+
onChanged();
|
|
463
|
+
}
|
|
464
|
+
catch {
|
|
465
|
+
restore.disabled = false;
|
|
466
|
+
restore.textContent = '取消归档';
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
row.append(content, restore);
|
|
470
|
+
return row;
|
|
471
|
+
}
|
|
472
|
+
function formatArchiveTime(timestamp) {
|
|
473
|
+
const value = timestamp < 10_000_000_000 ? timestamp * 1000 : timestamp;
|
|
474
|
+
try {
|
|
475
|
+
return new Intl.DateTimeFormat(undefined, { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(value));
|
|
476
|
+
}
|
|
477
|
+
catch {
|
|
478
|
+
return new Date(value).toLocaleString();
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function closeArchiveModal(dom) {
|
|
482
|
+
dom.querySelector(`[${WORKSPACE_SESSION_ARCHIVE_MODAL_ATTRIBUTE}]`)?.remove();
|
|
483
|
+
}
|
|
484
|
+
function normalizeRemote(value) {
|
|
485
|
+
if (!isRecord(value))
|
|
486
|
+
return {};
|
|
487
|
+
// DSH Client Remote 通常按 namespace 暴露;兼容部分宿主直接使用 service 名称。
|
|
488
|
+
const workspace = asRecord(readRemoteProperty(value, 'workspace'))
|
|
489
|
+
?? asRecord(readRemoteProperty(value, 'workspaceController'));
|
|
490
|
+
const session = asRecord(readRemoteProperty(value, 'session'))
|
|
491
|
+
?? asRecord(readRemoteProperty(value, 'sessionController'));
|
|
492
|
+
return {
|
|
493
|
+
...(workspace ? { workspace: workspace } : {}),
|
|
494
|
+
...(session ? { session: session } : {}),
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
/** Cordis Remote 是受注入约束的代理,读取未声明的可选 namespace 会抛异常。 */
|
|
498
|
+
function readRemoteProperty(value, key) {
|
|
499
|
+
try {
|
|
500
|
+
return value[key];
|
|
501
|
+
}
|
|
502
|
+
catch {
|
|
503
|
+
return undefined;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
function readString(value) {
|
|
507
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
508
|
+
}
|
|
509
|
+
function readStringArray(value) {
|
|
510
|
+
return Array.isArray(value) ? value.flatMap((item) => { const result = readString(item); return result === undefined ? [] : [result]; }) : [];
|
|
511
|
+
}
|
|
512
|
+
function asRecord(value) {
|
|
513
|
+
return isRecord(value) ? value : undefined;
|
|
514
|
+
}
|
|
515
|
+
/** DSH Remote 的直接调用返回 RemoteResult;测试桩和旧宿主可能直接返回 value。 */
|
|
516
|
+
function unwrapRemoteValue(value) {
|
|
517
|
+
const record = asRecord(value);
|
|
518
|
+
if (record === undefined || typeof record.ok !== 'boolean')
|
|
519
|
+
return value;
|
|
520
|
+
return record.ok ? record.value : undefined;
|
|
521
|
+
}
|
|
522
|
+
function isRecord(value) {
|
|
523
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
524
|
+
}
|
|
525
|
+
//# sourceMappingURL=workspace-session-archive-dom.js.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** 从 DSH 侧栏会话行对应的 React Fiber 向上查找会话身份。 */
|
|
2
|
+
export function resolveDshSessionId(row) {
|
|
3
|
+
const carrier = row;
|
|
4
|
+
const fiberKey = Object.getOwnPropertyNames(row).find((key) => key.startsWith('__reactFiber$'));
|
|
5
|
+
if (fiberKey === undefined)
|
|
6
|
+
return undefined;
|
|
7
|
+
let current = carrier[fiberKey];
|
|
8
|
+
for (let depth = 0; depth < 32 && isRecord(current); depth += 1) {
|
|
9
|
+
const fiber = current;
|
|
10
|
+
const sessionId = sessionIdFromProps(fiber.memoizedProps) ?? sessionIdFromProps(fiber.pendingProps);
|
|
11
|
+
if (sessionId !== undefined)
|
|
12
|
+
return sessionId;
|
|
13
|
+
current = fiber.return;
|
|
14
|
+
}
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
function sessionIdFromProps(value) {
|
|
18
|
+
if (!isRecord(value))
|
|
19
|
+
return undefined;
|
|
20
|
+
for (const key of ['node', 'result']) {
|
|
21
|
+
const candidate = value[key];
|
|
22
|
+
if (!isRecord(candidate) || typeof candidate.id !== 'string')
|
|
23
|
+
continue;
|
|
24
|
+
const sessionId = candidate.id.trim();
|
|
25
|
+
if (sessionId !== '')
|
|
26
|
+
return sessionId;
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
function isRecord(value) {
|
|
31
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=workspace-session-fiber.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ProviderVisual } from './provider-icons.js';
|
|
2
|
+
export declare const WORKSPACE_SESSION_ROW_SELECTOR = "[role=\"treeitem\"]";
|
|
3
|
+
export declare const WORKSPACE_SESSION_LOGO_ATTRIBUTE = "data-codingns-session-logo";
|
|
4
|
+
export interface WorkspaceSessionLogoDomController {
|
|
5
|
+
/** 映射变化或测试场景可显式触发一次扫描。 */
|
|
6
|
+
refresh(): void;
|
|
7
|
+
/** 断开观察器并移除当前文档中的所有插件节点。 */
|
|
8
|
+
dispose(): void;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkspaceSessionLogoDomOptions {
|
|
11
|
+
readonly document?: Document;
|
|
12
|
+
readonly MutationObserver?: typeof MutationObserver;
|
|
13
|
+
readonly adapterIdForSession?: (sessionId: string) => string | undefined;
|
|
14
|
+
readonly visualForAdapter?: (adapterId: string | undefined) => ProviderVisual;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* 为 DSH 0.1.6-alpha.2 会话行安装兼容注入器。
|
|
18
|
+
*
|
|
19
|
+
* 该版本没有 leading Slot,也没有把 sessionId 写进 DOM。这里仅读取 React
|
|
20
|
+
* Fiber 上 `node.id`/`result.id` 两个已验证字段;任何不匹配都直接跳过。
|
|
21
|
+
*/
|
|
22
|
+
export declare function startWorkspaceSessionLogoDom(options?: WorkspaceSessionLogoDomOptions): WorkspaceSessionLogoDomController;
|
|
23
|
+
/** 移除当前文档中全部由本模块插入的节点。 */
|
|
24
|
+
export declare function removeWorkspaceSessionLogos(dom: Pick<Document, 'querySelectorAll'>): void;
|
|
25
|
+
export { resolveDshSessionId } from './workspace-session-fiber.js';
|
|
26
|
+
//# sourceMappingURL=workspace-session-logo-dom.d.ts.map
|