dsh-over-ssh 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/agents/agent-manifest.json +1 -1
- package/lib/client.js +363 -314
- package/lib/file-references.js +80 -35
- package/lib/fs.js +158 -75
- package/lib/index.js +4103 -3397
- package/lib/terminal-wrapper.js +9 -4
- package/lib/types/src/config.d.ts +80 -9
- package/lib/types/src/file-references.d.ts +0 -1
- package/lib/types/src/host/app/backend.d.ts +9 -1
- package/lib/types/src/host/app/recycle-bin/recycle-bin-contract.d.ts +0 -1
- package/lib/types/src/host/app/recycle-bin/recycle-bin-service.d.ts +0 -1
- package/lib/types/src/host/app/session-admin/session-admin-contract.d.ts +12 -1
- package/lib/types/src/host/app/session-admin/session-admin-service.d.ts +0 -1
- package/lib/types/src/host/app/transport/transport-selector.d.ts +0 -1
- package/lib/types/src/host/app/transport/transport-service.d.ts +0 -1
- package/lib/types/src/host/domain/capability.d.ts +0 -1
- package/lib/types/src/host/domain/errors.d.ts +0 -1
- package/lib/types/src/host/domain/roots/connection.d.ts +0 -1
- package/lib/types/src/host/domain/roots/root.d.ts +24 -9
- package/lib/types/src/host/domain/roots/session-roots.d.ts +0 -1
- package/lib/types/src/host/domain/roots/ssh-config.d.ts +0 -1
- package/lib/types/src/host/domain/session/session-admin.d.ts +0 -1
- package/lib/types/src/host/domain/transfer/recycle-bin.d.ts +0 -1
- package/lib/types/src/host/domain/transfer/transfer.d.ts +0 -1
- package/lib/types/src/host/infra/transport/agent/agent-installer.d.ts +0 -1
- package/lib/types/src/host/infra/transport/ssh/ssh2-transport.d.ts +0 -1
- package/lib/types/src/host/infra/transport/transport-base.d.ts +0 -1
- package/lib/types/src/host/infra/transport/transport.d.ts +16 -2
- package/lib/types/src/host/integration/agent/agent-preflight.d.ts +0 -1
- package/lib/types/src/host/integration/fs/file-reference-provider.d.ts +0 -1
- package/lib/types/src/host/integration/fs/fs-provider.d.ts +0 -2
- package/lib/types/src/host/integration/transfer/transfer-service.d.ts +0 -1
- package/lib/types/src/index.d.ts +23 -1
- package/lib/types/src/local-bridge-refs.d.ts +0 -1
- package/lib/types/src/local-bridge.d.ts +0 -1
- package/lib/types/src/shared/root-keys.d.ts +120 -0
- package/lib/types/src/wiring/helpers.d.ts +0 -1
- package/package.json +5 -3
- package/lib/types/src/client/controls.d.ts +0 -131
- package/lib/types/src/client/core/card-focus-trap.d.ts +0 -45
- package/lib/types/src/client/core/controls.d.ts +0 -164
- package/lib/types/src/client/core/latest.d.ts +0 -48
- package/lib/types/src/client/core/locale.d.ts +0 -38
- package/lib/types/src/client/core/message.d.ts +0 -37
- package/lib/types/src/client/core/panel-model.d.ts +0 -134
- package/lib/types/src/client/core/portal.d.ts +0 -266
- package/lib/types/src/client/core/remote.d.ts +0 -84
- package/lib/types/src/client/core/styles.d.ts +0 -458
- package/lib/types/src/client/file-tree-io.d.ts +0 -19
- package/lib/types/src/client/file-tree.d.ts +0 -271
- package/lib/types/src/client/flows/directory-browser.d.ts +0 -587
- package/lib/types/src/client/flows/session-directory.d.ts +0 -415
- package/lib/types/src/client/flows/workspace-directory-flow.d.ts +0 -480
- package/lib/types/src/client/locale.d.ts +0 -31
- package/lib/types/src/client/panel-model.d.ts +0 -37
- package/lib/types/src/client/panel.d.ts +0 -77
- package/lib/types/src/client/roots/connection-test.d.ts +0 -69
- package/lib/types/src/client/roots/roots-panel-state.d.ts +0 -307
- package/lib/types/src/client/roots/roots-panel-view.d.ts +0 -280
- package/lib/types/src/client/roots/roots-panel.d.ts +0 -364
- package/lib/types/src/client/roots/transfer-panel-view.d.ts +0 -35
- package/lib/types/src/client/roots/transfer-panel.d.ts +0 -241
- package/lib/types/src/client/roots/transfer-progress.d.ts +0 -67
- package/lib/types/src/client/roots-panel-view.d.ts +0 -236
- package/lib/types/src/client/roots-panel.d.ts +0 -347
- package/lib/types/src/client/session/session-panel-view.d.ts +0 -336
- package/lib/types/src/client/session/session-panel.d.ts +0 -322
- package/lib/types/src/client/session/session-remote-mark.d.ts +0 -110
- package/lib/types/src/client/session/session-row-actions.d.ts +0 -398
- package/lib/types/src/client/session-panel.d.ts +0 -152
- package/lib/types/src/client/styles.d.ts +0 -252
- package/lib/types/src/client/workspace-directory-flow.d.ts +0 -342
- package/lib/types/src/fs.d.ts +0 -46
- package/lib/types/src/host/app/recycle-bin/recycle-bin-move.d.ts +0 -18
- package/lib/types/src/host/app/recycle-bin/recycle-bin-world.d.ts +0 -38
- package/lib/types/src/host/app/recycle-bin-contract.d.ts +0 -215
- package/lib/types/src/host/app/recycle-bin-move.d.ts +0 -18
- package/lib/types/src/host/app/recycle-bin-service.d.ts +0 -45
- package/lib/types/src/host/app/recycle-bin-world.d.ts +0 -35
- package/lib/types/src/host/app/roots/roots-api-connections.d.ts +0 -49
- package/lib/types/src/host/app/roots/roots-api-contract.d.ts +0 -369
- package/lib/types/src/host/app/roots/roots-api-directories.d.ts +0 -13
- package/lib/types/src/host/app/roots/roots-api-http.d.ts +0 -77
- package/lib/types/src/host/app/roots/roots-api-long-poll.d.ts +0 -130
- package/lib/types/src/host/app/roots/roots-api-projections.d.ts +0 -102
- package/lib/types/src/host/app/roots/roots-api-roots.d.ts +0 -13
- package/lib/types/src/host/app/roots/roots-api-state.d.ts +0 -22
- package/lib/types/src/host/app/roots/roots-api-transfers.d.ts +0 -26
- package/lib/types/src/host/app/roots/roots-api.d.ts +0 -55
- package/lib/types/src/host/app/roots/user-roots.d.ts +0 -552
- package/lib/types/src/host/app/roots-api-connections.d.ts +0 -49
- package/lib/types/src/host/app/roots-api-contract.d.ts +0 -339
- package/lib/types/src/host/app/roots-api-directories.d.ts +0 -13
- package/lib/types/src/host/app/roots-api-http.d.ts +0 -77
- package/lib/types/src/host/app/roots-api-projections.d.ts +0 -102
- package/lib/types/src/host/app/roots-api-roots.d.ts +0 -13
- package/lib/types/src/host/app/roots-api-state.d.ts +0 -22
- package/lib/types/src/host/app/roots-api-support.d.ts +0 -182
- package/lib/types/src/host/app/roots-api-transfers.d.ts +0 -13
- package/lib/types/src/host/app/roots-api.d.ts +0 -55
- package/lib/types/src/host/app/routed-fs.d.ts +0 -239
- package/lib/types/src/host/app/serialize.d.ts +0 -37
- package/lib/types/src/host/app/session-admin/session-admin-context.d.ts +0 -36
- package/lib/types/src/host/app/session-admin/session-admin-delete.d.ts +0 -34
- package/lib/types/src/host/app/session-admin-context.d.ts +0 -36
- package/lib/types/src/host/app/session-admin-contract.d.ts +0 -214
- package/lib/types/src/host/app/session-admin-delete.d.ts +0 -34
- package/lib/types/src/host/app/session-admin-service.d.ts +0 -38
- package/lib/types/src/host/app/takeover/takeover-plan.d.ts +0 -227
- package/lib/types/src/host/app/takeover/takeover-rows.d.ts +0 -55
- package/lib/types/src/host/app/transport-selector.d.ts +0 -98
- package/lib/types/src/host/app/transport-service.d.ts +0 -75
- package/lib/types/src/host/app/user-roots.d.ts +0 -461
- package/lib/types/src/host/domain/bin-location.d.ts +0 -79
- package/lib/types/src/host/domain/connection.d.ts +0 -95
- package/lib/types/src/host/domain/glob-pattern.d.ts +0 -62
- package/lib/types/src/host/domain/literal-text.d.ts +0 -63
- package/lib/types/src/host/domain/path-grammar.d.ts +0 -84
- package/lib/types/src/host/domain/recycle-bin.d.ts +0 -337
- package/lib/types/src/host/domain/root.d.ts +0 -74
- package/lib/types/src/host/domain/roots/bin-location.d.ts +0 -91
- package/lib/types/src/host/domain/roots/route-table.d.ts +0 -102
- package/lib/types/src/host/domain/route-table.d.ts +0 -80
- package/lib/types/src/host/domain/session/terminal-target.d.ts +0 -136
- package/lib/types/src/host/domain/session-admin.d.ts +0 -330
- package/lib/types/src/host/domain/shell.d.ts +0 -47
- package/lib/types/src/host/domain/ssh-config.d.ts +0 -73
- package/lib/types/src/host/domain/terminal-target.d.ts +0 -132
- package/lib/types/src/host/domain/tool-requests.d.ts +0 -369
- package/lib/types/src/host/domain/transfer.d.ts +0 -260
- package/lib/types/src/host/infra/backend/local-backend.d.ts +0 -84
- package/lib/types/src/host/infra/backend/local-version.d.ts +0 -43
- package/lib/types/src/host/infra/backend/platform-local-backend.d.ts +0 -89
- package/lib/types/src/host/infra/backend/remote-backend.d.ts +0 -239
- package/lib/types/src/host/infra/cache/lru.d.ts +0 -52
- package/lib/types/src/host/infra/cache/versioned.d.ts +0 -77
- package/lib/types/src/host/infra/temp-name.d.ts +0 -19
- package/lib/types/src/host/infra/transport/agent/agent-protocol-client.d.ts +0 -132
- package/lib/types/src/host/infra/transport/agent/agent-protocol-codec.d.ts +0 -140
- package/lib/types/src/host/infra/transport/agent/agent-protocol-errors.d.ts +0 -41
- package/lib/types/src/host/infra/transport/agent/agent-protocol-frames.d.ts +0 -197
- package/lib/types/src/host/infra/transport/agent/agent-protocol-types.d.ts +0 -564
- package/lib/types/src/host/infra/transport/agent/agent-transport.d.ts +0 -418
- package/lib/types/src/host/infra/transport/agent-installer.d.ts +0 -121
- package/lib/types/src/host/infra/transport/agent-protocol-client.d.ts +0 -123
- package/lib/types/src/host/infra/transport/agent-protocol-codec.d.ts +0 -131
- package/lib/types/src/host/infra/transport/agent-protocol-errors.d.ts +0 -41
- package/lib/types/src/host/infra/transport/agent-protocol-frames.d.ts +0 -197
- package/lib/types/src/host/infra/transport/agent-protocol-types.d.ts +0 -509
- package/lib/types/src/host/infra/transport/agent-transport.d.ts +0 -353
- package/lib/types/src/host/infra/transport/alias-transport.d.ts +0 -206
- package/lib/types/src/host/infra/transport/askpass-fs.d.ts +0 -20
- package/lib/types/src/host/infra/transport/askpass.d.ts +0 -133
- package/lib/types/src/host/infra/transport/command-pool.d.ts +0 -61
- package/lib/types/src/host/infra/transport/fs-commands.d.ts +0 -140
- package/lib/types/src/host/infra/transport/git-status-route.d.ts +0 -63
- package/lib/types/src/host/infra/transport/git-status.d.ts +0 -101
- package/lib/types/src/host/infra/transport/node-spawn.d.ts +0 -26
- package/lib/types/src/host/infra/transport/session-window.d.ts +0 -51
- package/lib/types/src/host/infra/transport/shell-quote.d.ts +0 -59
- package/lib/types/src/host/infra/transport/ssh/alias-transport.d.ts +0 -206
- package/lib/types/src/host/infra/transport/ssh/askpass-fs.d.ts +0 -20
- package/lib/types/src/host/infra/transport/ssh/askpass.d.ts +0 -133
- package/lib/types/src/host/infra/transport/ssh/shell-quote.d.ts +0 -62
- package/lib/types/src/host/infra/transport/ssh/ssh-argv.d.ts +0 -43
- package/lib/types/src/host/infra/transport/ssh-argv.d.ts +0 -43
- package/lib/types/src/host/infra/transport/ssh2-transport.d.ts +0 -302
- package/lib/types/src/host/integration/agent/agent-install-io.d.ts +0 -33
- package/lib/types/src/host/integration/agent/askpass-registry.d.ts +0 -66
- package/lib/types/src/host/integration/agent-install-io.d.ts +0 -33
- package/lib/types/src/host/integration/agent-preflight.d.ts +0 -56
- package/lib/types/src/host/integration/anchor-directory.d.ts +0 -56
- package/lib/types/src/host/integration/askpass-registry.d.ts +0 -59
- package/lib/types/src/host/integration/bin-root-resolver.d.ts +0 -81
- package/lib/types/src/host/integration/connection-test.d.ts +0 -73
- package/lib/types/src/host/integration/file-reference-provider.d.ts +0 -115
- package/lib/types/src/host/integration/fs/git-status-wiring.d.ts +0 -21
- package/lib/types/src/host/integration/fs/platform-errors.d.ts +0 -94
- package/lib/types/src/host/integration/fs/remote-file-tools.d.ts +0 -369
- package/lib/types/src/host/integration/fs/remote-index.d.ts +0 -177
- package/lib/types/src/host/integration/fs-provider.d.ts +0 -226
- package/lib/types/src/host/integration/lsp/lsp-framing.d.ts +0 -38
- package/lib/types/src/host/integration/lsp/lsp-registry.d.ts +0 -67
- package/lib/types/src/host/integration/lsp/lsp-session.d.ts +0 -130
- package/lib/types/src/host/integration/management-tools.d.ts +0 -64
- package/lib/types/src/host/integration/recycle-bin-service.d.ts +0 -247
- package/lib/types/src/host/integration/remote/connection-test.d.ts +0 -73
- package/lib/types/src/host/integration/remote/remote-face.d.ts +0 -127
- package/lib/types/src/host/integration/remote/remote-tool-backend.d.ts +0 -108
- package/lib/types/src/host/integration/remote-face.d.ts +0 -127
- package/lib/types/src/host/integration/remote-file-tools.d.ts +0 -241
- package/lib/types/src/host/integration/remote-home.d.ts +0 -69
- package/lib/types/src/host/integration/remote-index.d.ts +0 -165
- package/lib/types/src/host/integration/remote-job-tools.d.ts +0 -295
- package/lib/types/src/host/integration/remote-tool-backend.d.ts +0 -97
- package/lib/types/src/host/integration/remote-tools.d.ts +0 -206
- package/lib/types/src/host/integration/root-registry.d.ts +0 -115
- package/lib/types/src/host/integration/roots/anchor-directory.d.ts +0 -56
- package/lib/types/src/host/integration/roots/bin-root-resolver.d.ts +0 -81
- package/lib/types/src/host/integration/roots/remote-home.d.ts +0 -69
- package/lib/types/src/host/integration/roots/root-registry.d.ts +0 -144
- package/lib/types/src/host/integration/roots/roots-wiring.d.ts +0 -219
- package/lib/types/src/host/integration/roots/session-root-index.d.ts +0 -52
- package/lib/types/src/host/integration/roots-api.d.ts +0 -407
- package/lib/types/src/host/integration/roots-wiring.d.ts +0 -211
- package/lib/types/src/host/integration/session/session-api.d.ts +0 -88
- package/lib/types/src/host/integration/session/session-bin-lock.d.ts +0 -80
- package/lib/types/src/host/integration/session/session-header.d.ts +0 -151
- package/lib/types/src/host/integration/session/session-labels.d.ts +0 -56
- package/lib/types/src/host/integration/session/session-route-wiring.d.ts +0 -50
- package/lib/types/src/host/integration/session-admin-service.d.ts +0 -236
- package/lib/types/src/host/integration/session-api.d.ts +0 -81
- package/lib/types/src/host/integration/session-bin-lock.d.ts +0 -80
- package/lib/types/src/host/integration/session-header.d.ts +0 -118
- package/lib/types/src/host/integration/session-labels.d.ts +0 -56
- package/lib/types/src/host/integration/session-route-wiring.d.ts +0 -50
- package/lib/types/src/host/integration/session-scope.d.ts +0 -113
- package/lib/types/src/host/integration/session-tools.d.ts +0 -44
- package/lib/types/src/host/integration/strict-arguments.d.ts +0 -79
- package/lib/types/src/host/integration/takeover/takeover-api.d.ts +0 -62
- package/lib/types/src/host/integration/takeover/takeover-route-wiring.d.ts +0 -60
- package/lib/types/src/host/integration/takeover/takeover-service.d.ts +0 -167
- package/lib/types/src/host/integration/terminal-manifest.d.ts +0 -94
- package/lib/types/src/host/integration/tools/management-tools.d.ts +0 -245
- package/lib/types/src/host/integration/tools/remote-job-tools.d.ts +0 -316
- package/lib/types/src/host/integration/tools/remote-lsp-tools.d.ts +0 -43
- package/lib/types/src/host/integration/tools/remote-tools.d.ts +0 -236
- package/lib/types/src/host/integration/tools/session-target.d.ts +0 -52
- package/lib/types/src/host/integration/tools/session-tools.d.ts +0 -46
- package/lib/types/src/host/integration/tools/strict-arguments.d.ts +0 -79
- package/lib/types/src/host/integration/tools/terminal-manifest.d.ts +0 -94
- package/lib/types/src/host/integration/transfer/remote-copy.d.ts +0 -66
- package/lib/types/src/host/integration/transfer/transfer-adapters.d.ts +0 -48
- package/lib/types/src/host/integration/transfer/transfer-tracker.d.ts +0 -106
- package/lib/types/src/host/integration/transfer-adapters.d.ts +0 -44
- package/lib/types/src/host/integration/transfer-service.d.ts +0 -164
- package/lib/types/src/host/integration/transfer-tracker.d.ts +0 -106
- package/lib/types/src/host/integration/user-roots.d.ts +0 -461
- package/lib/types/src/wiring/connections.d.ts +0 -189
- package/lib/types/src/wiring/fs.d.ts +0 -13
- package/lib/types/src/wiring/install-memo.d.ts +0 -32
- package/lib/types/src/wiring/legacy-dirs.d.ts +0 -62
- package/lib/types/src/wiring/package-paths.d.ts +0 -35
- package/lib/types/src/wiring/roots.d.ts +0 -21
- package/lib/types/src/wiring/state-dir.d.ts +0 -29
- package/lib/types/src/wiring/tools.d.ts +0 -35
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The sidebar's **remote-session mark**: the small globe in front of a session row whose cwd is one of this
|
|
3
|
-
* plugin's anchor directories.
|
|
4
|
-
*
|
|
5
|
-
* Why this exists at all: the shell gives a remote workspace **no** way to look different. `dsh-client-ui-workspace`
|
|
6
|
-
* renders workspace rows with the fixed `IconFolderOpenRegular`/`IconFolderCloseRegular` primitives and declares
|
|
7
|
-
* no workspace-row slot, so a workspace opened on `<home>/.dsh/over-ssh/roots/<connection>/<digest>/<leaf>`
|
|
8
|
-
* (a real, empty local directory that the router maps to the remote root) is visually identical to
|
|
9
|
-
* `D:\project\anything`. The only seat the shell offers is the leading 16px cell of a **session** row,
|
|
10
|
-
* `sidebar.session.row.leading`, and per the platform's own contract that cell is mounted **only while the
|
|
11
|
-
* row's primary state is idle** — a running, unread or archived row shows its status dot (or nothing) there
|
|
12
|
-
* instead. The mark is therefore a hint, not an identification: it can be absent for a remote session that is
|
|
13
|
-
* busy. Documented rather than worked around; DOM surgery on the workspace row (what
|
|
14
|
-
* `@zhangfengshun/dsh-remote-ssh` does for its own workspaces) is not worth the fragility here.
|
|
15
|
-
*
|
|
16
|
-
* No React state and no hooks: the occupant receives a synchronous predicate through its registration's
|
|
17
|
-
* `inject` face, so this module stays a pure template that the offline renderer can serialize.
|
|
18
|
-
*
|
|
19
|
-
* @module dsh-over-ssh/client/session-remote-mark
|
|
20
|
-
*/
|
|
21
|
-
import { type ReactNode } from 'react';
|
|
22
|
-
/**
|
|
23
|
-
* The owner share of the seat, quoted from the platform (`SessionRowScheduleOwnerProps` in
|
|
24
|
-
* `@deepseek-ai/dsh-client-ui-workspace/client`, `packages/client/ui-workspace/src/client/contract/slots.ts`).
|
|
25
|
-
*
|
|
26
|
-
* Mirrored instead of imported, exactly like the directory-flow holes in
|
|
27
|
-
* `flows/workspace-directory-flow.ts`: that package is not a dependency of this repository (its types live in
|
|
28
|
-
* the DSH checkout), and the merge below has to compile here.
|
|
29
|
-
*/
|
|
30
|
-
export interface SessionRowLeadingOwnerProps {
|
|
31
|
-
/** Session this row shows; the occupant addresses its own data by this id. */
|
|
32
|
-
sessionId: string;
|
|
33
|
-
}
|
|
34
|
-
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
35
|
-
interface SlotMap {
|
|
36
|
-
/**
|
|
37
|
-
* Leading decoration of one Session row: the 16px cell before the title.
|
|
38
|
-
*
|
|
39
|
-
* Declared here because this repository does not depend on `@deepseek-ai/dsh-client-ui-workspace`, whose
|
|
40
|
-
* declaration would otherwise bring it in — the same reason the two directory-flow holes are declared by
|
|
41
|
-
* the flow module itself. The contract (list slot, root scope, idle-only, one owner prop) is the
|
|
42
|
-
* platform's; see the module doc for what that means for the mark's visibility.
|
|
43
|
-
*/
|
|
44
|
-
'sidebar.session.row.leading': {
|
|
45
|
-
kind: 'list';
|
|
46
|
-
scope: 'root';
|
|
47
|
-
owner: SessionRowLeadingOwnerProps;
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/** The seat this plugin occupies: one decoration cell in front of a session row's title. */
|
|
52
|
-
export declare const REMOTE_MARK_SLOT = "sidebar.session.row.leading";
|
|
53
|
-
/** Our own cell key. A fresh id is added **beside** the shipped entries; reusing one would replace it. */
|
|
54
|
-
export declare const REMOTE_MARK_ID = "over-ssh-remote-mark";
|
|
55
|
-
/**
|
|
56
|
-
* Position among the occupants of that cell.
|
|
57
|
-
*
|
|
58
|
-
* The shipped occupant is the schedule plugin's mark at order 10 ("has scheduled tasks"). A remote workspace
|
|
59
|
-
* is a property of where the session *lives*, not of what it is doing, so it goes first.
|
|
60
|
-
*/
|
|
61
|
-
export declare const REMOTE_MARK_ORDER = 5;
|
|
62
|
-
/** The minimal row shape the index reads: an id and the directory the session runs in. */
|
|
63
|
-
export interface SessionCwdRow {
|
|
64
|
-
readonly id: string;
|
|
65
|
-
readonly cwd: string | undefined;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Whether one session's cwd is inside the plugin's anchor root.
|
|
69
|
-
*
|
|
70
|
-
* Separators are folded (Windows serves both spellings, and the platform has been seen to normalize to `/`)
|
|
71
|
-
* and the comparison is **case-insensitive**. On a case-sensitive filesystem that fold could in principle
|
|
72
|
-
* accept a different directory that differs only by case; the anchor root is `<home>/.dsh/over-ssh/roots`,
|
|
73
|
-
* where that cannot happen in practice, and the alternative (guessing the platform's spelling) is worse.
|
|
74
|
-
*
|
|
75
|
-
* The boundary is explicit: `.../roots` matches, `.../roots/x` matches, `.../roots-old` does **not**.
|
|
76
|
-
*
|
|
77
|
-
* @param cwd - the session's working directory, as the host reported it.
|
|
78
|
-
* @param anchorRoot - the plugin's anchor root, from the roots snapshot; `undefined` means "unknown".
|
|
79
|
-
* @returns `true` only when the cwd is at or under the anchor root.
|
|
80
|
-
*/
|
|
81
|
-
export declare function isRemoteCwd(cwd: string | undefined, anchorRoot: string | undefined): boolean;
|
|
82
|
-
/**
|
|
83
|
-
* The set of session ids that live in a remote workspace.
|
|
84
|
-
*
|
|
85
|
-
* One pass over the inventory; the caller caches the result and re-registers the occupant when it changes.
|
|
86
|
-
*
|
|
87
|
-
* @param sessions - the session inventory rows (only `id` and `cwd` are read).
|
|
88
|
-
* @param anchorRoot - the plugin's anchor root, or `undefined` when the host did not answer one.
|
|
89
|
-
* @returns the ids to mark; empty when the anchor root is unknown.
|
|
90
|
-
*/
|
|
91
|
-
export declare function remoteSessionIds(sessions: readonly SessionCwdRow[], anchorRoot: string | undefined): ReadonlySet<string>;
|
|
92
|
-
/** What one occupancy of the seat is handed: the row's own prop plus this plugin's inject face. */
|
|
93
|
-
export interface RemoteSessionMarkProps {
|
|
94
|
-
/** The session this row shows (owner prop). */
|
|
95
|
-
readonly sessionId?: string;
|
|
96
|
-
/** Whether that session is one of ours (inject face). */
|
|
97
|
-
readonly isRemoteSession?: (sessionId: string) => boolean;
|
|
98
|
-
/** Localized label, used as both the tooltip and the accessible name (inject face). */
|
|
99
|
-
readonly label?: string;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* The globe, or nothing.
|
|
103
|
-
*
|
|
104
|
-
* A function component with no hooks on purpose: the settings-section views in this repository are called
|
|
105
|
-
* **directly** by their tests (outside a React render), and a hook would throw there.
|
|
106
|
-
*
|
|
107
|
-
* @param props - the row's session id plus the injected predicate and label.
|
|
108
|
-
* @returns the mark, or `null` for a local session (and for a composition that injects nothing).
|
|
109
|
-
*/
|
|
110
|
-
export declare function RemoteSessionMark(props: RemoteSessionMarkProps): ReactNode;
|
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The **sidebar session 「…」 menu**: the two actions this plugin adds to it, and the confirmation dialog they
|
|
3
|
-
* share.
|
|
4
|
-
*
|
|
5
|
-
* Why the menu at all: the platform's own menu offers pin / rename / fork / archive, and none of them removes a
|
|
6
|
-
* session from the store. The only way to reclaim the bytes a finished session keeps was the settings page
|
|
7
|
-
* (`session-panel-view.ts`), which means opening a modal and scrolling a list of 88 rows to act on the row the
|
|
8
|
-
* user is looking at. `sidebar.workspaces.session.menu.item` is the seat for "act on *this* session", so the
|
|
9
|
-
* same two actions live there.
|
|
10
|
-
*
|
|
11
|
-
* ## How the two seats fit together
|
|
12
|
-
*
|
|
13
|
-
* A menu item cannot render a dialog: the menu is a popover that closes as soon as an item is clicked, and
|
|
14
|
-
* anything it renders goes away with it. The platform's own `ArchiveSession` is built the same way, and this
|
|
15
|
-
* module copies that structure:
|
|
16
|
-
*
|
|
17
|
-
* 1. **The item only delivers a request.** {@link createRowActionHandler} closes the menu
|
|
18
|
-
* (`setMenuOpen(false)`) and writes a {@link SessionRowActionRequest} into a per-plugin store. No I/O, no
|
|
19
|
-
* dialog, no state of its own.
|
|
20
|
-
* 2. **A second registration renders the dialog** into `shell.overlay`, the global overlay layer. It subscribes
|
|
21
|
-
* to the same store, so "which action is pending" is one value rather than two components agreeing.
|
|
22
|
-
* 3. **A success closes itself.** The card keeps the answer for {@link SESSION_ROW_ACTION_AUTO_CLOSE_MS} and then
|
|
23
|
-
* drops the request: the user asked for the deletion, not for a dialog whose only content is "it worked". A
|
|
24
|
-
* failure never auto-closes — that sentence is the reason the card exists.
|
|
25
|
-
*
|
|
26
|
-
* ## Why the store is a factory, not a module singleton
|
|
27
|
-
*
|
|
28
|
-
* A module-level singleton would survive a plugin unload and would be shared by two plugin instances (the same
|
|
29
|
-
* reason `createRootsPanelController` is built per load). {@link createSessionRowActionStore} is created once by
|
|
30
|
-
* `apply` and disposed with it; the pending request therefore cannot leak across loads.
|
|
31
|
-
*
|
|
32
|
-
* ## The decision is the row's own
|
|
33
|
-
*
|
|
34
|
-
* Nothing here re-derives whether a session may be deleted: {@link SessionRowView.deletable} and
|
|
35
|
-
* {@link SessionRowView.blockedReason} come from the host through `session-panel.ts`, and the dialog only
|
|
36
|
-
* translates them into a sentence and a disabled button. A running session cannot be deleted (the host has an
|
|
37
|
-
* active turn), a sub-session cannot be deleted on its own (it follows its parent), an unreadable one cannot be
|
|
38
|
-
* judged. A session the host merely **has loaded** is deletable — that is the running/live split the settings
|
|
39
|
-
* panel also renders — and the dialog adds the warning that a later write recreates its log directory.
|
|
40
|
-
*
|
|
41
|
-
* ## Testability
|
|
42
|
-
*
|
|
43
|
-
* Every component here that holds a hook is a **thin shell**: the state machine is
|
|
44
|
-
* {@link createSessionRowActionStore}, the decision is {@link describeSessionRowAction}, and the markup is
|
|
45
|
-
* {@link SessionRowMenuItem} / {@link SessionRowActionDialog}. This repository's client tests call components
|
|
46
|
-
* directly, outside any React render (where a hook throws), so the parts a test needs are the hook-free ones.
|
|
47
|
-
*
|
|
48
|
-
* @module dsh-over-ssh/client/session/session-row-actions
|
|
49
|
-
*/
|
|
50
|
-
import { type ReactNode } from 'react';
|
|
51
|
-
import type { MessageKey } from '../core/message.ts';
|
|
52
|
-
import type { OverSshKey } from '../core/locale.ts';
|
|
53
|
-
import { type PanelActionResult, type SessionPanelIo, type SessionRowView } from './session-panel.ts';
|
|
54
|
-
/**
|
|
55
|
-
* The platform's menu-item seat: one `list` slot, root scope.
|
|
56
|
-
*
|
|
57
|
-
* Mirrored instead of imported, exactly like `session-remote-mark.ts` does for the row-leading cell: the slot is
|
|
58
|
-
* declared by `@deepseek-ai/dsh-client-ui-workspace`, which is **not** a dependency of this repository (its types
|
|
59
|
-
* live in the DSH checkout), and the registration below has to compile here.
|
|
60
|
-
*/
|
|
61
|
-
export interface SessionMenuOwnerProps {
|
|
62
|
-
/** The session this menu row is about; every action addresses its own data by this id. */
|
|
63
|
-
readonly sessionId: string;
|
|
64
|
-
/** The title the sidebar shows for that session, when it has one. */
|
|
65
|
-
readonly displayTitle?: string | undefined;
|
|
66
|
-
}
|
|
67
|
-
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
68
|
-
interface SlotMap {
|
|
69
|
-
/**
|
|
70
|
-
* One extra entry of a session row's 「…」 menu.
|
|
71
|
-
*
|
|
72
|
-
* The platform already occupies `pin` (100), `rename` (200), `fork` (300) and `archive` (400); `order`
|
|
73
|
-
* decides where a new entry lands, and the occupant receives `{ sessionId, displayTitle }` plus the
|
|
74
|
-
* platform's `useMenuOpenState` hook. This repository declares the key itself because the owning package is
|
|
75
|
-
* not a dependency here (see the module doc of `session-remote-mark.ts` for the same trade-off).
|
|
76
|
-
*/
|
|
77
|
-
'sidebar.workspaces.session.menu.item': {
|
|
78
|
-
kind: 'list';
|
|
79
|
-
scope: 'root';
|
|
80
|
-
owner: SessionMenuOwnerProps;
|
|
81
|
-
};
|
|
82
|
-
/**
|
|
83
|
-
* The global overlay layer: where a dialog opened **from a popover** has to live.
|
|
84
|
-
*
|
|
85
|
-
* The menu unmounts as soon as it closes, so a card rendered inside it would disappear with the click that
|
|
86
|
-
* opened it. Root scope and `list`, like the platform's own archive confirmation.
|
|
87
|
-
*/
|
|
88
|
-
'shell.overlay': {
|
|
89
|
-
kind: 'list';
|
|
90
|
-
scope: 'root';
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
/** The menu-item seat. */
|
|
95
|
-
export declare const SESSION_MENU_SLOT = "sidebar.workspaces.session.menu.item";
|
|
96
|
-
/** 「移动到回收站」. A fresh id sits **beside** the shipped entries; reusing one would replace it. */
|
|
97
|
-
export declare const SESSION_ROW_RECYCLE_ID = "over-ssh-session-recycle";
|
|
98
|
-
/**
|
|
99
|
-
* Position of 「移动到回收站」 among the menu entries.
|
|
100
|
-
*
|
|
101
|
-
* The platform's own entries are 100/200/300/400 (pin, rename, fork, archive). Both of ours go after them: they
|
|
102
|
-
* are destructive and must not sit between the user and the actions they use every day.
|
|
103
|
-
*/
|
|
104
|
-
export declare const SESSION_ROW_RECYCLE_ORDER = 500;
|
|
105
|
-
/** 「删除会话」 — the irreversible twin of {@link SESSION_ROW_RECYCLE_ID}. */
|
|
106
|
-
export declare const SESSION_ROW_DELETE_ID = "over-ssh-session-delete";
|
|
107
|
-
/** Position of 「删除会话」: right below the recycle action, and after every shipped entry. */
|
|
108
|
-
export declare const SESSION_ROW_DELETE_ORDER = 510;
|
|
109
|
-
/** The overlay seat the confirmation dialog is rendered into. */
|
|
110
|
-
export declare const SESSION_ROW_ACTIONS_OVERLAY_SLOT = "shell.overlay";
|
|
111
|
-
/** Stable overlay entry id, so a locale-driven re-registration replaces the same entry. */
|
|
112
|
-
export declare const SESSION_ROW_ACTIONS_OVERLAY_ID = "over-ssh-session-actions-overlay";
|
|
113
|
-
/** Order of the dialog among the overlay's occupants (it is modal, so it does not matter much; stable is what does). */
|
|
114
|
-
export declare const SESSION_ROW_ACTIONS_OVERLAY_ORDER = 500;
|
|
115
|
-
/** The heading's `id` when the shell did not pass a per-mount one (direct template calls, tests). */
|
|
116
|
-
export declare const SESSION_ROW_ACTIONS_TITLE_ID = "dsh-rw-session-action-title";
|
|
117
|
-
/** Which of the two actions a request is about. */
|
|
118
|
-
export type SessionRowActionKind = 'recycle' | 'delete';
|
|
119
|
-
/** What a menu item hands to the store. */
|
|
120
|
-
export interface SessionRowActionRequest {
|
|
121
|
-
readonly kind: SessionRowActionKind;
|
|
122
|
-
readonly sessionId: string;
|
|
123
|
-
/** The title the sidebar showed; the dialog's sentence names the session by it. */
|
|
124
|
-
readonly displayTitle: string | undefined;
|
|
125
|
-
}
|
|
126
|
-
/** Why the dialog cannot answer yet, and what it decided once it could. */
|
|
127
|
-
export type SessionRowActionPhase = 'idle' | 'loading' | 'ready' | 'unavailable';
|
|
128
|
-
/** The whole dialog state. */
|
|
129
|
-
export interface SessionRowActionState {
|
|
130
|
-
/** The pending request; `undefined` means the dialog is closed. */
|
|
131
|
-
readonly request: SessionRowActionRequest | undefined;
|
|
132
|
-
readonly phase: SessionRowActionPhase;
|
|
133
|
-
/** The row the request named, once the inventory answered; `undefined` also means "not found". */
|
|
134
|
-
readonly row: SessionRowView | undefined;
|
|
135
|
-
/** True while the confirmed action is in flight. */
|
|
136
|
-
readonly busy: boolean;
|
|
137
|
-
/** The last answer: the success sentence or the host's error text. */
|
|
138
|
-
readonly outcome: PanelActionResult | undefined;
|
|
139
|
-
}
|
|
140
|
-
/** Text the menu items and the dialog show; the caller supplies the active locale's dictionary. */
|
|
141
|
-
export interface SessionRowActionsCopy {
|
|
142
|
-
/** Menu label of 「移动到回收站」, also the dialog's title. */
|
|
143
|
-
readonly recycle: string;
|
|
144
|
-
/** Menu label of 「删除会话」, also the dialog's title. */
|
|
145
|
-
readonly delete: string;
|
|
146
|
-
/** The sentence naming what the recycle action will do. */
|
|
147
|
-
readonly recycleBody: (title: string, id: string) => string;
|
|
148
|
-
/** The sentence naming what the permanent delete will do. */
|
|
149
|
-
readonly deleteBody: (title: string, id: string) => string;
|
|
150
|
-
readonly confirmRecycle: string;
|
|
151
|
-
readonly confirmDelete: string;
|
|
152
|
-
readonly cancel: string;
|
|
153
|
-
readonly close: string;
|
|
154
|
-
readonly loading: string;
|
|
155
|
-
readonly blockedRunning: string;
|
|
156
|
-
readonly blockedSubsession: string;
|
|
157
|
-
readonly blockedUnreadable: string;
|
|
158
|
-
/** The row is not in the inventory any more (it was deleted elsewhere). */
|
|
159
|
-
readonly missing: string;
|
|
160
|
-
/** The inventory itself could not be read, so nothing can be decided. */
|
|
161
|
-
readonly unavailable: string;
|
|
162
|
-
/**
|
|
163
|
-
* The "still loaded by the host" warning, shared with the settings panel's permanent-delete confirmation:
|
|
164
|
-
* one sentence for one fact, wherever it is shown.
|
|
165
|
-
*/
|
|
166
|
-
readonly loadedWarning: (count: number) => string;
|
|
167
|
-
/** The label of the platform's no-workspace bucket (`sessionLabel` returns `''` there). */
|
|
168
|
-
readonly workspaceUnknown: string;
|
|
169
|
-
/**
|
|
170
|
-
* Resolve one deferred message.
|
|
171
|
-
*
|
|
172
|
-
* The action result comes out of `session-panel.ts` as a `MessageKey`, because that module has no dictionary —
|
|
173
|
-
* the sentences it used to spell were Chinese in both locales (audit 2026-10-31 client.md §2.3).
|
|
174
|
-
*/
|
|
175
|
-
readonly resolve: (message: MessageKey) => string;
|
|
176
|
-
}
|
|
177
|
-
/** What a menu item is handed: our inject face plus the platform's owner props and hook. */
|
|
178
|
-
export interface SessionRowActionsInject {
|
|
179
|
-
/** The active locale's dictionary. */
|
|
180
|
-
readonly copy: SessionRowActionsCopy;
|
|
181
|
-
/** The per-plugin store (created once by `apply`). */
|
|
182
|
-
readonly store: SessionRowActionStore;
|
|
183
|
-
}
|
|
184
|
-
/** The platform's menu-state hook: `const [, setMenuOpen] = useMenuOpenState()`. */
|
|
185
|
-
export type SessionMenuOpenStateHook = () => readonly [boolean, (open: boolean) => void];
|
|
186
|
-
/** Props of the menu-item shell. */
|
|
187
|
-
export interface SessionRowActionsContainerProps extends SessionRowActionsInject {
|
|
188
|
-
/** Which of the two items this occurrence is (from its own registration). */
|
|
189
|
-
readonly kind: SessionRowActionKind;
|
|
190
|
-
/** The session this row is about (owner prop of the seat). */
|
|
191
|
-
readonly sessionId?: string | undefined;
|
|
192
|
-
/** The title the sidebar shows (owner prop of the seat). */
|
|
193
|
-
readonly displayTitle?: string | undefined;
|
|
194
|
-
/** The platform's hook; `undefined` in a composition that does not hand it out. */
|
|
195
|
-
readonly useMenuOpenState?: SessionMenuOpenStateHook | undefined;
|
|
196
|
-
}
|
|
197
|
-
/** Props of the hook-free menu item. */
|
|
198
|
-
export interface SessionRowMenuItemProps {
|
|
199
|
-
readonly label: string;
|
|
200
|
-
/** Stable DOM id (the item's own registration id). */
|
|
201
|
-
readonly itemId: string;
|
|
202
|
-
readonly onClick: () => void;
|
|
203
|
-
/**
|
|
204
|
-
* Render as destructive (red label).
|
|
205
|
-
*
|
|
206
|
-
* The platform's own menu rows are neutral, so a red one has to earn it: this is only for an action that cannot
|
|
207
|
-
* be undone from the UI. 「移动到回收站」 is reversible (the recycle bin restores it), 「删除会话」 is not.
|
|
208
|
-
*/
|
|
209
|
-
readonly danger?: boolean;
|
|
210
|
-
}
|
|
211
|
-
/** Props of the dialog shell: the inject face only. */
|
|
212
|
-
export type SessionRowActionsOverlayProps = SessionRowActionsInject;
|
|
213
|
-
/** What the menu items call. */
|
|
214
|
-
export interface RowActionHandlerOptions {
|
|
215
|
-
readonly kind: SessionRowActionKind;
|
|
216
|
-
readonly sessionId: string;
|
|
217
|
-
readonly displayTitle?: string | undefined;
|
|
218
|
-
/** The platform's menu setter, when the composition hands one out. */
|
|
219
|
-
readonly setMenuOpen?: ((open: boolean) => void) | undefined;
|
|
220
|
-
readonly request: (request: SessionRowActionRequest) => void;
|
|
221
|
-
}
|
|
222
|
-
/** What the dialog renders. Every string is already resolved; the template composes no text of its own. */
|
|
223
|
-
export interface SessionRowActionView {
|
|
224
|
-
readonly kind: SessionRowActionKind;
|
|
225
|
-
readonly title: string;
|
|
226
|
-
readonly body: string;
|
|
227
|
-
/** The "still loaded by the host" warning, or `''` when it does not apply. */
|
|
228
|
-
readonly warning: string;
|
|
229
|
-
/** Why the action may not run, or `''` when it may. */
|
|
230
|
-
readonly blocked: string;
|
|
231
|
-
/** Whether the target allows the action at all (the policy decision, independent of `busy`). */
|
|
232
|
-
readonly confirmable: boolean;
|
|
233
|
-
/** Which buttons the card offers: the confirm/cancel pair, or close only. */
|
|
234
|
-
readonly buttons: 'confirm' | 'close';
|
|
235
|
-
readonly busy: boolean;
|
|
236
|
-
readonly loading: boolean;
|
|
237
|
-
readonly outcome: string;
|
|
238
|
-
readonly outcomeTone: 'info' | 'error';
|
|
239
|
-
readonly confirmLabel: string;
|
|
240
|
-
readonly cancelLabel: string;
|
|
241
|
-
readonly closeLabel: string;
|
|
242
|
-
}
|
|
243
|
-
/** What the store needs: the injected calls (the same `io` the settings panel uses). */
|
|
244
|
-
export interface SessionRowActionStoreDeps {
|
|
245
|
-
readonly io: SessionPanelIo;
|
|
246
|
-
/**
|
|
247
|
-
* Schedule one delayed close and return its cancel.
|
|
248
|
-
*
|
|
249
|
-
* Injected so a test drives the delay instead of waiting on a timer, and so the dialog's lifetime is a value
|
|
250
|
-
* rather than a `setTimeout` buried in a component.
|
|
251
|
-
*/
|
|
252
|
-
readonly schedule?: (run: () => void, delayMs: number) => () => void;
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* How long a successful answer stays on screen before the dialog closes itself.
|
|
256
|
-
*
|
|
257
|
-
* Long enough to read one short sentence ("已永久删除 43.6 KiB(不可恢复)"), short enough that dismissing the card
|
|
258
|
-
* is not a second click the user has to make. **Failures never auto-close** — an error is the one thing worth
|
|
259
|
-
* reading, and it must not disappear while the user is still parsing it.
|
|
260
|
-
*/
|
|
261
|
-
export declare const SESSION_ROW_ACTION_AUTO_CLOSE_MS = 1500;
|
|
262
|
-
/** The per-plugin store the menu items write to and the overlay subscribes to. */
|
|
263
|
-
export interface SessionRowActionStore {
|
|
264
|
-
readonly getState: () => SessionRowActionState;
|
|
265
|
-
readonly subscribe: (listener: () => void) => () => void;
|
|
266
|
-
/** Hand a request over; the dialog opens on the next read. */
|
|
267
|
-
readonly request: (request: SessionRowActionRequest) => void;
|
|
268
|
-
/** Read the inventory row the pending request names. */
|
|
269
|
-
readonly load: () => Promise<void>;
|
|
270
|
-
/** Run the confirmed action and record its answer. */
|
|
271
|
-
readonly confirm: () => Promise<void>;
|
|
272
|
-
/** Drop the request (the dialog closes). */
|
|
273
|
-
readonly close: () => void;
|
|
274
|
-
/** Re-arm after a mount (React's development replay runs the cleanup once before the real lifetime). */
|
|
275
|
-
readonly activate: () => void;
|
|
276
|
-
readonly dispose: () => void;
|
|
277
|
-
}
|
|
278
|
-
/** The state of a store that has never been written to. */
|
|
279
|
-
export declare function initialSessionRowActionState(): SessionRowActionState;
|
|
280
|
-
/**
|
|
281
|
-
* The identity of one dialog occupancy: which action, on which session, or that the dialog is closed.
|
|
282
|
-
*
|
|
283
|
-
* A named pure function rather than a bare dependency array, because this repository's tests do not render
|
|
284
|
-
* hooks: an effect's dependency list cannot be executed by a test, so the *decision* ("a new request owes a
|
|
285
|
-
* fresh read") is extracted here where it can be. It is the same reasoning as `rootsQueryKey` in the session
|
|
286
|
-
* entry.
|
|
287
|
-
*
|
|
288
|
-
* @param state - the store's state.
|
|
289
|
-
* @returns `closed` with no pending request, otherwise a key that changes with the request.
|
|
290
|
-
*/
|
|
291
|
-
export declare function actionQueryKey(state: SessionRowActionState): string;
|
|
292
|
-
/**
|
|
293
|
-
* Build the stateful half: pending request, inventory read, the confirmed call and its answer.
|
|
294
|
-
*
|
|
295
|
-
* `confirm` deliberately re-checks the row instead of trusting the click: the menu and the dialog are two
|
|
296
|
-
* different surfaces, and between the two the session may have started running. The host refuses such a delete
|
|
297
|
-
* anyway; refusing it here is what keeps the dialog from promising something the route would reject.
|
|
298
|
-
*
|
|
299
|
-
* @param deps - the injected calls.
|
|
300
|
-
* @returns the store; every method is safe after {@link SessionRowActionStore.dispose}.
|
|
301
|
-
*/
|
|
302
|
-
export declare function createSessionRowActionStore(deps: SessionRowActionStoreDeps): SessionRowActionStore;
|
|
303
|
-
/**
|
|
304
|
-
* The click handler a menu item uses: close the menu, then hand the request over.
|
|
305
|
-
*
|
|
306
|
-
* The order is the whole point. The dialog lives in `shell.overlay`, but the menu is a popover **above** the
|
|
307
|
-
* page; leaving it open would put it on top of the card and swallow the card's own clicks. Extracted as a pure
|
|
308
|
-
* factory so the ordering is a fact a test can hold on to — inline in a component, no test could reach it.
|
|
309
|
-
*
|
|
310
|
-
* @param options - the action, its target, the platform's menu setter and the store's writer.
|
|
311
|
-
* @returns the click handler.
|
|
312
|
-
*/
|
|
313
|
-
export declare function createRowActionHandler(options: RowActionHandlerOptions): () => void;
|
|
314
|
-
/**
|
|
315
|
-
* The menu entry one action kind renders: its stable DOM id and whether it is destructive.
|
|
316
|
-
*
|
|
317
|
-
* Extracted from {@link SessionRowActionsContainer} because that container calls the platform's hook, and this
|
|
318
|
-
* repository's client tests cannot render a hook-using component — "which id, and is it red" is a decision, and a
|
|
319
|
-
* decision left in the shell is a decision no test can reach.
|
|
320
|
-
*/
|
|
321
|
-
export interface SessionRowMenuEntry {
|
|
322
|
-
readonly itemId: string;
|
|
323
|
-
readonly danger: boolean;
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* The menu entry for one action kind.
|
|
327
|
-
*
|
|
328
|
-
* Only 「删除会话」 is destructive: it is permanent. 「移动到回收站」 can be undone from the recycle bin, so making
|
|
329
|
-
* it red would teach the user to ignore red.
|
|
330
|
-
*
|
|
331
|
-
* @param kind - which action this row offers.
|
|
332
|
-
* @returns the item id and the destructive flag.
|
|
333
|
-
*/
|
|
334
|
-
export declare function sessionRowMenuEntry(kind: SessionRowActionKind): SessionRowMenuEntry;
|
|
335
|
-
/**
|
|
336
|
-
* The badge-free menu entry: one `role="menuitem"` button.
|
|
337
|
-
*
|
|
338
|
-
* Hook-free on purpose (the tests call it directly), and it renders no icon: the platform's own menu rows are
|
|
339
|
-
* text, and a glyph would need a theme token this plugin does not have.
|
|
340
|
-
*
|
|
341
|
-
* @param props - the label, the stable DOM id, the click handler and the destructive flag.
|
|
342
|
-
* @returns the menu item button.
|
|
343
|
-
*/
|
|
344
|
-
export declare function SessionRowMenuItem(props: SessionRowMenuItemProps): ReactNode;
|
|
345
|
-
/**
|
|
346
|
-
* The menu item as the platform renders it: the owner props, the injected face and the menu hook.
|
|
347
|
-
*
|
|
348
|
-
* The hook is called only when the composition hands it out. That is the same trade-off the session entry
|
|
349
|
-
* documents for its framework seats (`readWorkspacePath`): the *presence* of a seat is fixed per composition,
|
|
350
|
-
* so the call cannot appear and disappear between two renders of one mount.
|
|
351
|
-
*
|
|
352
|
-
* @param props - the seat's props plus this plugin's inject face.
|
|
353
|
-
* @returns the menu item, or `null` when the seat handed no session id (nothing to act on).
|
|
354
|
-
*/
|
|
355
|
-
export declare function SessionRowActionsContainer(props: SessionRowActionsContainerProps): ReactNode;
|
|
356
|
-
/**
|
|
357
|
-
* Turn the store's state into the strings and the button set the dialog renders.
|
|
358
|
-
*
|
|
359
|
-
* One function for the whole decision, so the wording, the refusal reason and the button set can never
|
|
360
|
-
* disagree: the branches are the four states of {@link SessionRowActionPhase} plus the row's own
|
|
361
|
-
* {@link SessionRowView.blockedReason}.
|
|
362
|
-
*
|
|
363
|
-
* @param state - the store's state.
|
|
364
|
-
* @param copy - the active locale's dictionary.
|
|
365
|
-
* @returns the view, or `undefined` while no request is pending (the dialog renders nothing).
|
|
366
|
-
*/
|
|
367
|
-
export declare function describeSessionRowAction(state: SessionRowActionState, copy: SessionRowActionsCopy): SessionRowActionView | undefined;
|
|
368
|
-
/** Props of the hook-free dialog template. */
|
|
369
|
-
export interface SessionRowActionDialogProps {
|
|
370
|
-
readonly view: SessionRowActionView;
|
|
371
|
-
readonly onConfirm: () => void;
|
|
372
|
-
readonly onClose: () => void;
|
|
373
|
-
/** `id` of the heading, referenced by `aria-labelledby`; the shell passes a per-mount id. */
|
|
374
|
-
readonly titleId?: string | undefined;
|
|
375
|
-
}
|
|
376
|
-
/**
|
|
377
|
-
* The confirmation card: title, the sentence naming the target, the refusal or the warning, and its buttons.
|
|
378
|
-
*
|
|
379
|
-
* Rendered **in the overlay**, not in the menu: see the module doc. Esc closes it wherever the focus is inside
|
|
380
|
-
* the card (the shell puts it there when the card opens).
|
|
381
|
-
*
|
|
382
|
-
* @param props - the view, what the two buttons call, and the heading's id.
|
|
383
|
-
* @returns the dialog card.
|
|
384
|
-
*/
|
|
385
|
-
export declare function SessionRowActionDialog({ view, onConfirm, onClose, titleId, }: SessionRowActionDialogProps): ReactNode;
|
|
386
|
-
/**
|
|
387
|
-
* The overlay occupant: subscribe to the store and render the dialog (portalled to `document.body`).
|
|
388
|
-
*
|
|
389
|
-
* Portalled rather than rendered in place, for the reason `portal.ts` documents at length: the sidebar clips its
|
|
390
|
-
* overflow, and a `position: fixed` card inside a clipped flex column is squeezed. Opening also moves focus into
|
|
391
|
-
* the card and remembers what held it, so Esc works and closing hands focus back to the menu that opened it.
|
|
392
|
-
*
|
|
393
|
-
* @param props - the injected dictionary and store.
|
|
394
|
-
* @returns the dialog inside its portal, or `null` while nothing is pending.
|
|
395
|
-
*/
|
|
396
|
-
export declare function SessionRowActionsOverlay(props: SessionRowActionsOverlayProps): ReactNode;
|
|
397
|
-
/** The dictionary keys this module renders, so a test can assert the two locales carry them. */
|
|
398
|
-
export type SessionRowActionsKey = Extract<OverSshKey, 'sessionRowRecycle' | 'sessionRowDelete' | 'sessionRowRecycleBody' | 'sessionRowDeleteBody' | 'sessionRowConfirmRecycle' | 'sessionRowConfirmDelete' | 'sessionRowCancel' | 'sessionRowClose' | 'sessionRowLoading' | 'sessionRowBlockedRunning' | 'sessionRowBlockedSubsession' | 'sessionRowBlockedUnreadable' | 'sessionRowMissing' | 'sessionRowUnavailable' | 'sessionPanelLiveWarning'>;
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The session panel's whole decision surface: wire parsing, HTTP calls, validation and the view model.
|
|
3
|
-
* **No React, no DOM, no `@deepseek-ai/*`** — the component renders what this module returns (红线 2).
|
|
4
|
-
*
|
|
5
|
-
* ## Three rules this module owns
|
|
6
|
-
*
|
|
7
|
-
* 1. **`GET` is the only read.** The panel never mutates on render; every destructive action is an explicit
|
|
8
|
-
* user click that becomes a `POST`.
|
|
9
|
-
* 2. **Irreversible actions carry `confirm: true`.** The host route refuses `purge` and
|
|
10
|
-
* `mode: 'permanent'` without it, so the panel must send it — and it only does so after the user
|
|
11
|
-
* confirms. Two locks, not one.
|
|
12
|
-
* 3. **A sub-session is not an orphan.** The model keeps `subsession` rows visibly distinct and refuses to
|
|
13
|
-
* offer them a delete button, because deleting one alone is what the requirement warns against.
|
|
14
|
-
*
|
|
15
|
-
* @module dsh-remote-workspace/client/session-panel
|
|
16
|
-
*/
|
|
17
|
-
/**
|
|
18
|
-
* Where the host route lives, as the browser must request it.
|
|
19
|
-
*
|
|
20
|
-
* **Duplicated from the host module on purpose**, exactly like `ROOTS_API_PATH` in `roots-panel.ts`: the
|
|
21
|
-
* client bundle must not pull host code in (it would drag `node:fs`/`node:child_process` into the browser
|
|
22
|
-
* build), and the two halves are separate bundles. The value is the contract; both sides assert it in their
|
|
23
|
-
* own tests, so a divergence is a red test rather than a silent 404.
|
|
24
|
-
*
|
|
25
|
-
* Relative on purpose: `dsh web` serves the page from the host origin, and the Desktop shell forwards
|
|
26
|
-
* `/api/...` from `dsh-app://app/` to the owned host with its session cookie (see `roots-panel.ts` for the
|
|
27
|
-
* full reasoning).
|
|
28
|
-
*/
|
|
29
|
-
export declare const SESSION_API_PATH = "/api/remote-workspace/sessions";
|
|
30
|
-
/** One inventory row as the panel uses it. */
|
|
31
|
-
export interface SessionRowView {
|
|
32
|
-
readonly id: string;
|
|
33
|
-
readonly classification: 'normal' | 'subsession' | 'unreadable';
|
|
34
|
-
readonly parentSessionId: string | undefined;
|
|
35
|
-
readonly title: string | undefined;
|
|
36
|
-
readonly cwd: string | undefined;
|
|
37
|
-
readonly bytes: number;
|
|
38
|
-
readonly updatedAtMs: number;
|
|
39
|
-
readonly live: boolean;
|
|
40
|
-
/** Whether this row may be deleted on its own (a live or unreadable row may not). */
|
|
41
|
-
readonly deletable: boolean;
|
|
42
|
-
/** Why it may not be deleted, when it may not. */
|
|
43
|
-
readonly blockedReason: 'live' | 'subsession' | 'unreadable' | undefined;
|
|
44
|
-
}
|
|
45
|
-
/** One bin row as the panel uses it. */
|
|
46
|
-
export interface BinRowView {
|
|
47
|
-
readonly entryId: string;
|
|
48
|
-
readonly sessionId: string;
|
|
49
|
-
readonly title: string | undefined;
|
|
50
|
-
readonly bytes: number;
|
|
51
|
-
readonly deletedAtMs: number;
|
|
52
|
-
readonly movedBy: 'rename' | 'copy';
|
|
53
|
-
}
|
|
54
|
-
/** The whole panel state. */
|
|
55
|
-
export interface SessionPanelSnapshot {
|
|
56
|
-
readonly available: boolean;
|
|
57
|
-
readonly sessions: readonly SessionRowView[];
|
|
58
|
-
readonly bin: readonly BinRowView[];
|
|
59
|
-
readonly policy: {
|
|
60
|
-
readonly maxEntries: number;
|
|
61
|
-
readonly maxBytes: number;
|
|
62
|
-
readonly maxAgeMs: number;
|
|
63
|
-
};
|
|
64
|
-
/** Totals the header shows without recomputing in the component. */
|
|
65
|
-
readonly totals: {
|
|
66
|
-
readonly sessions: number;
|
|
67
|
-
readonly bytes: number;
|
|
68
|
-
readonly subsessions: number;
|
|
69
|
-
readonly live: number;
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
/** What the panel needs from the browser, injected so a test needs no `fetch`. */
|
|
73
|
-
export interface SessionPanelIo {
|
|
74
|
-
readonly fetch: (path: string, init?: {
|
|
75
|
-
readonly method?: string;
|
|
76
|
-
readonly body?: string;
|
|
77
|
-
readonly headers?: Record<string, string>;
|
|
78
|
-
}) => Promise<{
|
|
79
|
-
readonly ok: boolean;
|
|
80
|
-
readonly status: number;
|
|
81
|
-
json: () => Promise<unknown>;
|
|
82
|
-
}>;
|
|
83
|
-
}
|
|
84
|
-
/** A panel action's outcome, as the view renders it. */
|
|
85
|
-
export type PanelActionResult = {
|
|
86
|
-
readonly kind: 'ok';
|
|
87
|
-
readonly message: string;
|
|
88
|
-
} | {
|
|
89
|
-
readonly kind: 'error';
|
|
90
|
-
readonly message: string;
|
|
91
|
-
readonly status?: number;
|
|
92
|
-
} | {
|
|
93
|
-
readonly kind: 'needs-confirmation';
|
|
94
|
-
readonly message: string;
|
|
95
|
-
};
|
|
96
|
-
/** Human byte formatting (binary units, one decimal). */
|
|
97
|
-
export declare function formatBytes(bytes: number): string;
|
|
98
|
-
/** Classify one raw row and decide whether it is deletable on its own. */
|
|
99
|
-
export declare function toSessionRowView(raw: Record<string, unknown>): SessionRowView | undefined;
|
|
100
|
-
/** Project one raw bin row. */
|
|
101
|
-
export declare function toBinRowView(raw: Record<string, unknown>): BinRowView | undefined;
|
|
102
|
-
/** An empty snapshot, used before the first answer and when the route is unavailable. */
|
|
103
|
-
export declare function emptySnapshot(): SessionPanelSnapshot;
|
|
104
|
-
/**
|
|
105
|
-
* Parse one `GET` answer.
|
|
106
|
-
*
|
|
107
|
-
* Field by field, like `parseRootsSnapshot`: a host answer carrying extra keys cannot reach the DOM.
|
|
108
|
-
*
|
|
109
|
-
* @param value - the decoded JSON body.
|
|
110
|
-
* @returns the snapshot; an unusable body yields {@link emptySnapshot}.
|
|
111
|
-
*/
|
|
112
|
-
export declare function parseSessionSnapshot(value: unknown): SessionPanelSnapshot;
|
|
113
|
-
/**
|
|
114
|
-
* Load the panel's data.
|
|
115
|
-
*
|
|
116
|
-
* @param io - injected fetch.
|
|
117
|
-
* @returns the snapshot; an HTTP failure is reported as `available: false` rather than thrown, because a
|
|
118
|
-
* panel that cannot reach the host must still render (and say so).
|
|
119
|
-
*/
|
|
120
|
-
export declare function loadSessionPanel(io: SessionPanelIo): Promise<SessionPanelSnapshot>;
|
|
121
|
-
/**
|
|
122
|
-
* Delete sessions.
|
|
123
|
-
*
|
|
124
|
-
* @param io - injected fetch.
|
|
125
|
-
* @param ids - session ids to delete.
|
|
126
|
-
* @param options - `permanent` requires `confirm: true`, which the caller only passes after the user agreed.
|
|
127
|
-
* @returns the outcome as the view renders it.
|
|
128
|
-
*/
|
|
129
|
-
export declare function deleteSessions(io: SessionPanelIo, ids: readonly string[], options?: {
|
|
130
|
-
readonly permanent?: boolean;
|
|
131
|
-
readonly includeSubsessions?: boolean;
|
|
132
|
-
}): Promise<PanelActionResult>;
|
|
133
|
-
/** Restore one bin entry. */
|
|
134
|
-
export declare function restoreSession(io: SessionPanelIo, entryId: string): Promise<PanelActionResult>;
|
|
135
|
-
/** Permanently remove one bin entry. Requires the caller to have confirmed. */
|
|
136
|
-
export declare function purgeSession(io: SessionPanelIo, entryId: string): Promise<PanelActionResult>;
|
|
137
|
-
/** Apply the retention policy. */
|
|
138
|
-
export declare function sweepSessions(io: SessionPanelIo): Promise<PanelActionResult>;
|
|
139
|
-
/** Preview a deletion without performing it. */
|
|
140
|
-
export declare function previewDelete(io: SessionPanelIo, ids: readonly string[], options?: {
|
|
141
|
-
readonly includeSubsessions?: boolean;
|
|
142
|
-
}): Promise<{
|
|
143
|
-
readonly delete: readonly string[];
|
|
144
|
-
readonly refusals: readonly {
|
|
145
|
-
readonly id: string;
|
|
146
|
-
readonly reason: string;
|
|
147
|
-
}[];
|
|
148
|
-
readonly bytes: number;
|
|
149
|
-
}>;
|
|
150
|
-
/** Whether an action needs the user to confirm before it may run. */
|
|
151
|
-
export declare function needsConfirmation(action: 'delete-permanent' | 'purge'): boolean;
|
|
152
|
-
//# sourceMappingURL=session-panel.d.ts.map
|