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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-over-ssh",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "SSH remote development for DeepSeek Harness: connections (key/password/ProxyJump/ssh config), remote files without sync, remote terminal, remote workspaces, remote @-completion, upload/download.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -56,12 +56,14 @@
|
|
|
56
56
|
"typecheck:client": "tsc -p tsconfig.client.json --pretty false",
|
|
57
57
|
"build:client": "node tools/build-client.mjs --check",
|
|
58
58
|
"build:host": "node tools/build-host.mjs",
|
|
59
|
-
"build": "npm run build:host && npm run build:
|
|
59
|
+
"build": "npm run build:host && npm run build:types && npm run build:terminal-wrapper && npm run build:client",
|
|
60
60
|
"agents": "node tools/agent-manifest.mjs generate --from ../dsh-over-ssh-agent/dist --out agents/agent-manifest.json --copy-to agents --platforms linux-amd64,linux-arm64 --require-provenance",
|
|
61
61
|
"build:types:client": "node ./node_modules/typescript/bin/tsc -p tsconfig.client.types.json --pretty false",
|
|
62
62
|
"prepack": "npm run build && node tools/prepack-agents.mjs && node tools/release-note.mjs --check",
|
|
63
63
|
"build:terminal-wrapper": "node tools/build-terminal-wrapper.mjs",
|
|
64
|
-
"release:note": "node tools/release-note.mjs"
|
|
64
|
+
"release:note": "node tools/release-note.mjs",
|
|
65
|
+
"build:types:host": "node ./node_modules/typescript/bin/tsc -p tsconfig.host.types.json",
|
|
66
|
+
"build:types": "node tools/clean-types.mjs && npm run build:types:host && npm run build:types:client && node tools/prune-types.mjs"
|
|
65
67
|
},
|
|
66
68
|
"peerDependencies": {
|
|
67
69
|
"@deepseek-ai/cordis": "^4.0.4",
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The controls the three surfaces share, as plain `createElement` templates.
|
|
3
|
-
*
|
|
4
|
-
* **No hooks, on purpose.** `:hover`, `:active`, `:focus-visible` and `:disabled` come from the stylesheet in
|
|
5
|
-
* [`styles.ts`](./styles.ts) via class names, because a component holding `useState` cannot be rendered by
|
|
6
|
-
* this repository's tests: they call the section functions **directly**, outside any React render, where a
|
|
7
|
-
* hook throws `Invalid hook call`. Classes keep the templates pure element trees — a test walks the returned
|
|
8
|
-
* tree and reads the same `type` / `name` / `disabled` / `role` it always did — while the browser still gets
|
|
9
|
-
* real interaction states.
|
|
10
|
-
*
|
|
11
|
-
* These are deliberately **not** the platform's `dsh-client-ui-primitives` atoms: those factories import
|
|
12
|
-
* per-component CSS modules, which a third-party bundle cannot supply (the loader materialises only
|
|
13
|
-
* registered factories), and the package is not a dependency of this repository. `styles.ts` carries the
|
|
14
|
-
* tokens those atoms are built from.
|
|
15
|
-
*
|
|
16
|
-
* @module dsh-remote-workspace/client/controls
|
|
17
|
-
*/
|
|
18
|
-
import { type ChangeEvent, type ReactNode } from 'react';
|
|
19
|
-
/** The sizes a control may have. */
|
|
20
|
-
export type ControlSize = 'normal' | 'compact';
|
|
21
|
-
/** The visual weight of a button. */
|
|
22
|
-
export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'row' | 'chip';
|
|
23
|
-
/** Props every control shares: extra classes from the caller. */
|
|
24
|
-
export interface ControlClassProps {
|
|
25
|
-
/** Extra classes from the caller; appended after the vocabulary's own. */
|
|
26
|
-
readonly className?: string;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* The part of a keyboard event a card's Esc handler reads.
|
|
30
|
-
*
|
|
31
|
-
* Structural, so the pure templates need no DOM types and a test drives the handler with a plain object. It is
|
|
32
|
-
* assignable from React's `KeyboardEvent` (which carries both members), and it is the whole reason a dialog
|
|
33
|
-
* closes on Esc: the shell focuses into the card when it opens, so the event reaches the card's own handler.
|
|
34
|
-
*/
|
|
35
|
-
export interface KeyEventLike {
|
|
36
|
-
/** The key name, e.g. `'Escape'`. */
|
|
37
|
-
readonly key?: string;
|
|
38
|
-
/** Stop the platform's own Esc handling from also firing while the dialog is up. */
|
|
39
|
-
stopPropagation?(): void;
|
|
40
|
-
}
|
|
41
|
-
/** True when the event is the Esc key. */
|
|
42
|
-
export declare function isEscape(event: KeyEventLike): boolean;
|
|
43
|
-
/**
|
|
44
|
-
* One button.
|
|
45
|
-
*
|
|
46
|
-
* @param props - the button's own props plus the variants this plugin uses.
|
|
47
|
-
* @returns the button element.
|
|
48
|
-
*/
|
|
49
|
-
export declare function Button(props: ControlClassProps & {
|
|
50
|
-
readonly variant: ButtonVariant;
|
|
51
|
-
readonly size?: ControlSize;
|
|
52
|
-
readonly type?: 'button' | 'submit';
|
|
53
|
-
readonly disabled?: boolean;
|
|
54
|
-
readonly name?: string;
|
|
55
|
-
readonly title?: string;
|
|
56
|
-
readonly onClick?: () => void;
|
|
57
|
-
readonly children?: ReactNode;
|
|
58
|
-
}): ReactNode;
|
|
59
|
-
/** Props of a text input, narrowed to what the templates pass. */
|
|
60
|
-
export interface TextInputProps extends ControlClassProps {
|
|
61
|
-
readonly id: string;
|
|
62
|
-
readonly name: string;
|
|
63
|
-
readonly value: string;
|
|
64
|
-
readonly disabled?: boolean;
|
|
65
|
-
readonly placeholder?: string;
|
|
66
|
-
readonly inputMode?: 'numeric';
|
|
67
|
-
/** `password` for a secret field: the browser masks it and no autofill offers to save it. */
|
|
68
|
-
readonly type?: 'text' | 'password';
|
|
69
|
-
readonly autoComplete?: 'off' | 'new-password';
|
|
70
|
-
readonly size?: ControlSize;
|
|
71
|
-
readonly onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* One text input, with the platform's border, inset surface and focus ring.
|
|
75
|
-
*
|
|
76
|
-
* @param props - the input's own props.
|
|
77
|
-
* @returns the input element.
|
|
78
|
-
*/
|
|
79
|
-
export declare function TextInput(props: TextInputProps): ReactNode;
|
|
80
|
-
/** One option of a {@link Select}. */
|
|
81
|
-
export interface SelectOption {
|
|
82
|
-
readonly value: string;
|
|
83
|
-
readonly label: string;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* A `<select>` over a fixed list of options.
|
|
87
|
-
*
|
|
88
|
-
* @param props - id, name, the current value, the options and the change handler.
|
|
89
|
-
* @returns the select element.
|
|
90
|
-
*/
|
|
91
|
-
export declare function Select(props: ControlClassProps & {
|
|
92
|
-
readonly id: string;
|
|
93
|
-
readonly name: string;
|
|
94
|
-
readonly value: string;
|
|
95
|
-
readonly options: readonly SelectOption[];
|
|
96
|
-
readonly disabled?: boolean;
|
|
97
|
-
readonly size?: ControlSize;
|
|
98
|
-
readonly onChange: (value: string) => void;
|
|
99
|
-
}): ReactNode;
|
|
100
|
-
/**
|
|
101
|
-
* A labelled field: the caption above its control, so the column and its label move together.
|
|
102
|
-
*
|
|
103
|
-
* @param props - the label text, the control's id and the control itself.
|
|
104
|
-
* @returns the labelled column.
|
|
105
|
-
*/
|
|
106
|
-
export declare function Field(props: {
|
|
107
|
-
readonly label: string;
|
|
108
|
-
readonly htmlFor: string;
|
|
109
|
-
readonly children: ReactNode;
|
|
110
|
-
}): ReactNode;
|
|
111
|
-
/**
|
|
112
|
-
* An inline notice block: the surface for a refusal, a host warning or "no host data".
|
|
113
|
-
*
|
|
114
|
-
* @param props - `warn` for something the operator may need to act on, `info` for a neutral status.
|
|
115
|
-
* @returns the notice element carrying the role its caller needs (`alert` / `note` / `status`).
|
|
116
|
-
*/
|
|
117
|
-
export declare function Notice(props: {
|
|
118
|
-
readonly role: 'alert' | 'note' | 'status';
|
|
119
|
-
readonly tone?: 'warn' | 'info';
|
|
120
|
-
readonly children: ReactNode;
|
|
121
|
-
}): ReactNode;
|
|
122
|
-
/**
|
|
123
|
-
* The status dot of a connection row, so the row's state is not carried by colour alone.
|
|
124
|
-
*
|
|
125
|
-
* @param props - the dot's accessible description.
|
|
126
|
-
* @returns the dot element.
|
|
127
|
-
*/
|
|
128
|
-
export declare function StatusDot(props: {
|
|
129
|
-
readonly label: string;
|
|
130
|
-
}): ReactNode;
|
|
131
|
-
//# sourceMappingURL=controls.d.ts.map
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The **one** Tab trap every card in this plugin installs.
|
|
3
|
-
*
|
|
4
|
-
* Five surfaces are dialogs and every one of them has to keep Tab inside itself, for two different reasons:
|
|
5
|
-
*
|
|
6
|
-
* - the settings-hosted cards sit **outside** the platform settings modal, whose own focus trap listens on
|
|
7
|
-
* `document` in the bubble phase and pulls focus back into its panel — a bubble-phase listener of ours would
|
|
8
|
-
* run after it (registration order) and could no longer tell which field Tab came from, so the trap has to
|
|
9
|
-
* be in the **capture** phase (audit 0090);
|
|
10
|
-
* - the overlay and composer cards are simply modals: Tab walking into the page behind them is a dialog the
|
|
11
|
-
* keyboard user leaves without dismissing.
|
|
12
|
-
*
|
|
13
|
-
* Both cases want the same mechanics, so they live here instead of being spelled out per surface (the two that
|
|
14
|
-
* were first — the connection editor and the session-panel confirmation — each carried their own copy of the
|
|
15
|
-
* same eight lines, and the two newer ones had none at all; audit 2026-10-31 `client.md` §2.3).
|
|
16
|
-
*
|
|
17
|
-
* The split is the same one the rest of `core/` uses: {@link installCardTabTrap} is pure and takes the document
|
|
18
|
-
* as an argument (so an offline test drives it with a fake), and {@link useCardTabTrap} is the two-line React
|
|
19
|
-
* binding.
|
|
20
|
-
*
|
|
21
|
-
* @module dsh-over-ssh/client/card-focus-trap
|
|
22
|
-
*/
|
|
23
|
-
import { type PortalDocument } from './portal.ts';
|
|
24
|
-
/**
|
|
25
|
-
* Install the card's Tab trap on one document.
|
|
26
|
-
*
|
|
27
|
-
* Capture phase on purpose: see the module note. The listener is removed with the same `options` value, so a
|
|
28
|
-
* runtime that keys listeners by phase cannot leak one.
|
|
29
|
-
*
|
|
30
|
-
* @param doc - the page's document, or `undefined` off-browser (where this is a no-op).
|
|
31
|
-
* @param container - the card's body-level container; `undefined` while it is not attached.
|
|
32
|
-
* @returns the remover; safe to call more than once.
|
|
33
|
-
*/
|
|
34
|
-
export declare function installCardTabTrap(doc: PortalDocument | undefined, container: unknown): () => void;
|
|
35
|
-
/**
|
|
36
|
-
* Trap Tab while a card is open.
|
|
37
|
-
*
|
|
38
|
-
* `open` is the caller's own condition (`editingId !== undefined`, `pending !== undefined`, `request !==
|
|
39
|
-
* undefined`); the container comes from that surface's portal. Nothing is installed while the card is closed, so
|
|
40
|
-
* the settings panel keeps its own Tab behaviour the rest of the time.
|
|
41
|
-
*
|
|
42
|
-
* @param open - whether the card is up.
|
|
43
|
-
* @param container - the card's container from its portal.
|
|
44
|
-
*/
|
|
45
|
-
export declare function useCardTabTrap(open: boolean, container: unknown): void;
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The controls the three surfaces share, as plain `createElement` templates.
|
|
3
|
-
*
|
|
4
|
-
* **No hooks, on purpose.** `:hover`, `:active`, `:focus-visible` and `:disabled` come from the stylesheet in
|
|
5
|
-
* [`styles.ts`](./styles.ts) via class names, because a component holding `useState` cannot be rendered by
|
|
6
|
-
* this repository's tests: they call the section functions **directly**, outside any React render, where a
|
|
7
|
-
* hook throws `Invalid hook call`. Classes keep the templates pure element trees — a test walks the returned
|
|
8
|
-
* tree and reads the same `type` / `name` / `disabled` / `role` it always did — while the browser still gets
|
|
9
|
-
* real interaction states.
|
|
10
|
-
*
|
|
11
|
-
* These are deliberately **not** the platform's `dsh-client-ui-primitives` atoms: those factories import
|
|
12
|
-
* per-component CSS modules, which a third-party bundle cannot supply (the loader materialises only
|
|
13
|
-
* registered factories), and the package is not a dependency of this repository. `styles.ts` carries the
|
|
14
|
-
* tokens those atoms are built from.
|
|
15
|
-
*
|
|
16
|
-
* @module dsh-over-ssh/client/controls
|
|
17
|
-
*/
|
|
18
|
-
import { type ChangeEvent, type ReactNode } from 'react';
|
|
19
|
-
/** The sizes a control may have. */
|
|
20
|
-
export type ControlSize = 'normal' | 'compact';
|
|
21
|
-
/** The visual weight of a button. */
|
|
22
|
-
export type ButtonVariant = 'primary' | 'secondary' | 'ghost' | 'row' | 'chip';
|
|
23
|
-
/** Props every control shares: extra classes from the caller. */
|
|
24
|
-
export interface ControlClassProps {
|
|
25
|
-
/** Extra classes from the caller; appended after the vocabulary's own. */
|
|
26
|
-
readonly className?: string;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* The part of a keyboard event a card's Esc handler reads.
|
|
30
|
-
*
|
|
31
|
-
* Structural, so the pure templates need no DOM types and a test drives the handler with a plain object. It is
|
|
32
|
-
* assignable from React's `KeyboardEvent` (which carries both members), and it is the whole reason a dialog
|
|
33
|
-
* closes on Esc: the shell focuses into the card when it opens, so the event reaches the card's own handler.
|
|
34
|
-
*/
|
|
35
|
-
export interface KeyEventLike {
|
|
36
|
-
/** The key name, e.g. `'Escape'`. */
|
|
37
|
-
readonly key?: string;
|
|
38
|
-
/** Stop the platform's own Esc handling from also firing while the dialog is up. */
|
|
39
|
-
stopPropagation?(): void;
|
|
40
|
-
}
|
|
41
|
-
/** True when the event is the Esc key. */
|
|
42
|
-
export declare function isEscape(event: KeyEventLike): boolean;
|
|
43
|
-
/**
|
|
44
|
-
* One button.
|
|
45
|
-
*
|
|
46
|
-
* @param props - the button's own props plus the variants this plugin uses.
|
|
47
|
-
* @returns the button element.
|
|
48
|
-
*/
|
|
49
|
-
export declare function Button(props: ControlClassProps & {
|
|
50
|
-
readonly variant: ButtonVariant;
|
|
51
|
-
readonly size?: ControlSize;
|
|
52
|
-
readonly type?: 'button' | 'submit';
|
|
53
|
-
readonly disabled?: boolean;
|
|
54
|
-
readonly name?: string;
|
|
55
|
-
readonly title?: string;
|
|
56
|
-
/** The name a screen reader reads instead of the visible text, when the text alone is not a name. */
|
|
57
|
-
readonly ariaLabel?: string;
|
|
58
|
-
/** For a control that opens something: whether it is open right now (`aria-expanded`). */
|
|
59
|
-
readonly ariaExpanded?: boolean;
|
|
60
|
-
/** What the control opens (`aria-haspopup`). */
|
|
61
|
-
readonly ariaHaspopup?: 'dialog' | 'menu';
|
|
62
|
-
readonly onClick?: () => void;
|
|
63
|
-
readonly children?: ReactNode;
|
|
64
|
-
}): ReactNode;
|
|
65
|
-
/** Props of a text input, narrowed to what the templates pass. */
|
|
66
|
-
export interface TextInputProps extends ControlClassProps {
|
|
67
|
-
readonly id: string;
|
|
68
|
-
readonly name: string;
|
|
69
|
-
readonly value: string;
|
|
70
|
-
readonly disabled?: boolean;
|
|
71
|
-
readonly placeholder?: string;
|
|
72
|
-
readonly inputMode?: 'numeric';
|
|
73
|
-
/** `password` for a secret field: the browser masks it and no autofill offers to save it. */
|
|
74
|
-
readonly type?: 'text' | 'password';
|
|
75
|
-
readonly autoComplete?: 'off' | 'new-password';
|
|
76
|
-
readonly size?: ControlSize;
|
|
77
|
-
readonly onChange: (event: ChangeEvent<HTMLInputElement>) => void;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* One text input, with the platform's border, inset surface and focus ring.
|
|
81
|
-
*
|
|
82
|
-
* @param props - the input's own props.
|
|
83
|
-
* @returns the input element.
|
|
84
|
-
*/
|
|
85
|
-
export declare function TextInput(props: TextInputProps): ReactNode;
|
|
86
|
-
/** One option of a {@link Select}. */
|
|
87
|
-
export interface SelectOption {
|
|
88
|
-
readonly value: string;
|
|
89
|
-
readonly label: string;
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* A `<select>` over a fixed list of options.
|
|
93
|
-
*
|
|
94
|
-
* @param props - id, name, the current value, the options and the change handler.
|
|
95
|
-
* @returns the select element.
|
|
96
|
-
*/
|
|
97
|
-
export declare function Select(props: ControlClassProps & {
|
|
98
|
-
readonly id: string;
|
|
99
|
-
readonly name: string;
|
|
100
|
-
readonly value: string;
|
|
101
|
-
readonly options: readonly SelectOption[];
|
|
102
|
-
readonly disabled?: boolean;
|
|
103
|
-
readonly size?: ControlSize;
|
|
104
|
-
readonly onChange: (value: string) => void;
|
|
105
|
-
}): ReactNode;
|
|
106
|
-
/**
|
|
107
|
-
* A labelled field: the caption above its control, so the column and its label move together.
|
|
108
|
-
*
|
|
109
|
-
* @param props - the label text, the control's id and the control itself.
|
|
110
|
-
* @returns the labelled column.
|
|
111
|
-
*/
|
|
112
|
-
export declare function Field(props: {
|
|
113
|
-
readonly label: string;
|
|
114
|
-
readonly htmlFor: string;
|
|
115
|
-
readonly children: ReactNode;
|
|
116
|
-
}): ReactNode;
|
|
117
|
-
/**
|
|
118
|
-
* An inline notice block: the surface for a refusal, a host warning or "no host data".
|
|
119
|
-
*
|
|
120
|
-
* Three tones, and the split is deliberate: **`warn` is the default** (something the operator may need to act
|
|
121
|
-
* on), `info` is a neutral status, and `error` is a hard failure. Before this, a failed read and a soft
|
|
122
|
-
* warning rendered identically — both `role: 'alert'` on the amber surface — so "the host refused" and
|
|
123
|
-
* "the host is telling you something" looked the same. A failure is not a warning.
|
|
124
|
-
*
|
|
125
|
-
* @param props - the tone, the role, and the text.
|
|
126
|
-
* @returns the notice element carrying the role its caller needs (`alert` / `note` / `status`).
|
|
127
|
-
*/
|
|
128
|
-
export declare function Notice(props: {
|
|
129
|
-
readonly role: 'alert' | 'note' | 'status';
|
|
130
|
-
readonly tone?: 'warn' | 'info' | 'error';
|
|
131
|
-
readonly children: ReactNode;
|
|
132
|
-
}): ReactNode;
|
|
133
|
-
/**
|
|
134
|
-
* A checkbox whose whole row is the hit target.
|
|
135
|
-
*
|
|
136
|
-
* The templates used to render a bare `<input type="checkbox">`: the stylesheet had **no rule for it at all**,
|
|
137
|
-
* so it kept the browser's default size and accent colour, and — in the session inventory — it sat next to a
|
|
138
|
-
* body that was not a `<label>`, so clicking the row's own text did nothing and the target was a 16px square.
|
|
139
|
-
*
|
|
140
|
-
* `id`/`name` are optional because the two call sites differ: the ssh-config list already pairs the box with a
|
|
141
|
-
* separate `<label htmlFor>` naming the alias, while the session row wants the box and its body wrapped
|
|
142
|
-
* together. Passing neither is allowed; passing `id` makes the input addressable by a sibling label.
|
|
143
|
-
*
|
|
144
|
-
* @param props - the box's identity, state and handler.
|
|
145
|
-
* @returns the input element.
|
|
146
|
-
*/
|
|
147
|
-
export declare function Checkbox(props: {
|
|
148
|
-
readonly checked: boolean;
|
|
149
|
-
readonly disabled?: boolean;
|
|
150
|
-
readonly id?: string;
|
|
151
|
-
readonly name?: string;
|
|
152
|
-
readonly onChange: () => void;
|
|
153
|
-
/** The accessible name, when no `<label>` wraps or names this box. */
|
|
154
|
-
readonly ariaLabel?: string;
|
|
155
|
-
}): ReactNode;
|
|
156
|
-
/**
|
|
157
|
-
* The status dot of a connection row, so the row's state is not carried by colour alone.
|
|
158
|
-
*
|
|
159
|
-
* @param props - the dot's accessible description.
|
|
160
|
-
* @returns the dot element.
|
|
161
|
-
*/
|
|
162
|
-
export declare function StatusDot(props: {
|
|
163
|
-
readonly label: string;
|
|
164
|
-
}): ReactNode;
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The "only the newest answer may land" gate.
|
|
3
|
-
*
|
|
4
|
-
* Why a module instead of a `let token = 0` in each caller: this repository's client tests **cannot render a
|
|
5
|
-
* React component** (no `react-dom`, see [`portal.ts`](./portal.ts)), so a generation counter written inside a
|
|
6
|
-
* component's `useCallback` is a decision no test can reach. Extracting it makes "a late answer is dropped"
|
|
7
|
-
* something a test can hold on to, which is the same reason `actionQueryKey` / `rootsQueryKey` /
|
|
8
|
-
* long-poll re-arms exist.
|
|
9
|
-
*
|
|
10
|
-
* The three call sites share one failure mode: two requests are in flight, the **older** one answers last, and
|
|
11
|
-
* its answer overwrites the newer state. That is not a theoretical race here — every one of these surfaces has
|
|
12
|
-
* a button that starts a second request while the first is still open (a refresh during an action, a re-read
|
|
13
|
-
* after a scope change, a poll racing a manual refresh).
|
|
14
|
-
*
|
|
15
|
-
* ```text
|
|
16
|
-
* const gate = createLatestOnly();
|
|
17
|
-
* const mine = gate.begin();
|
|
18
|
-
* const answer = await call();
|
|
19
|
-
* if (!gate.isCurrent(mine)) return; // a newer request owns the surface now
|
|
20
|
-
* ```
|
|
21
|
-
*
|
|
22
|
-
* @module dsh-over-ssh/client/latest
|
|
23
|
-
*/
|
|
24
|
-
/** A monotonic gate that admits only the most recently issued request. */
|
|
25
|
-
export interface LatestOnly {
|
|
26
|
-
/**
|
|
27
|
-
* Issue a new request id.
|
|
28
|
-
*
|
|
29
|
-
* @returns the id this request must present when it answers.
|
|
30
|
-
*/
|
|
31
|
-
begin(): number;
|
|
32
|
-
/**
|
|
33
|
-
* Whether an issued id is still the newest.
|
|
34
|
-
*
|
|
35
|
-
* @param token - the id `begin` returned to the request that is answering.
|
|
36
|
-
* @returns `true` only for the newest id; every earlier one is stale.
|
|
37
|
-
*/
|
|
38
|
-
isCurrent(token: number): boolean;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Build a gate.
|
|
42
|
-
*
|
|
43
|
-
* Ids start at 1, so `0` (a value a caller can get from an uninitialised variable) is never "current" by
|
|
44
|
-
* accident.
|
|
45
|
-
*
|
|
46
|
-
* @returns the gate.
|
|
47
|
-
*/
|
|
48
|
-
export declare function createLatestOnly(): LatestOnly;
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Client-half copy for the `over-ssh` namespace.
|
|
3
|
-
*
|
|
4
|
-
* Registration is the **typed** form of `ctx.locale.register`: the namespace is merged into
|
|
5
|
-
* `LocaleNamespaceMap`, so a missing key in either shipped locale is a compile error, and the runtime
|
|
6
|
-
* additionally requires both locales at registration (bilingual balance). Keeping the dictionaries in a
|
|
7
|
-
* pure module is what makes them testable without a browser.
|
|
8
|
-
*
|
|
9
|
-
* @module dsh-over-ssh/client/locale
|
|
10
|
-
*/
|
|
11
|
-
/** Namespace owned by this plugin's client half. */
|
|
12
|
-
export declare const OVER_SSH_NS = "over-ssh";
|
|
13
|
-
/**
|
|
14
|
-
* Keys of this namespace.
|
|
15
|
-
*
|
|
16
|
-
* `LocaleNamespaceMap` maps a namespace to its **key union** (not to a value interface): the runtime then
|
|
17
|
-
* derives `Record<Key, string>` per locale, which is exactly what makes a missing translation a compile
|
|
18
|
-
* error.
|
|
19
|
-
*/
|
|
20
|
-
export type OverSshKey = 'sectionTitle' | 'sectionDescription' | 'noHostData'
|
|
21
|
-
/** The connection list's own heading / empty state / unknown-transport badge (audit ui P2-6, P2-7). */
|
|
22
|
-
| 'connectionsHeading' | 'connectionsEmpty' | 'connectionsTransportUnknown' | 'close' | 'rootsTitle' | 'rootsDescription' | 'rootsAdvancedSummary' | 'rootsAdvancedNote' | 'rootsEmpty' | 'rootsPending' | 'rootsAuthPassword' | 'rootsPasswordLabel' | 'rootsTestConnection' | 'rootsTesting' | 'rootsTestOk' | 'sessionPanelTitle' | 'sessionPanelDescription' | 'sessionPanelUnavailable' | 'sessionPanelLoading' | 'sessionPanelLoadFailed' | 'sessionPanelSessionsEmpty' | 'sessionPanelSessions' | 'sessionPanelBin' | 'sessionPanelBinEmpty' | 'sessionPanelSelectAll' | 'sessionPanelClear' | 'sessionPanelDeleteRecycle' | 'sessionPanelDeletePermanent' | 'sessionPanelRestore' | 'sessionPanelPurge' | 'sessionPanelSweep' | 'sessionPanelRefresh' | 'sessionPanelClose' | 'sessionPanelLive' | 'sessionPanelSubsession' | 'sessionPanelUnreadable' | 'sessionPanelConfirmTitle' | 'sessionPanelConfirmBody' | 'sessionPanelConfirmPurgeBody' | 'sessionPanelConfirmCancel' | 'sessionPanelConfirmAccept' | 'sessionPanelDeleteWorkspace' | 'sessionPanelConfirmWorkspaceBody' | 'sessionPanelSubsessionsHidden' | 'sessionPanelTotals' | 'sessionPanelOpen' | 'sessionPanelLiveWarning' | 'sessionRowRecycle' | 'sessionRowDelete' | 'sessionRowRecycleBody' | 'sessionRowDeleteBody' | 'sessionRowConfirmRecycle' | 'sessionRowConfirmDelete' | 'sessionRowCancel' | 'sessionRowClose' | 'sessionRowLoading' | 'sessionRowBlockedRunning' | 'sessionRowBlockedSubsession' | 'sessionRowBlockedUnreadable' | 'sessionRowMissing' | 'sessionRowUnavailable' | 'rootsTestDegraded' | 'rootsTestFailed' | 'rootsAddConnection' | 'rootsRemove' | 'rootsSave' | 'rootsEdit' | 'rootsEditTitle' | 'rootsEditSave' | 'rootsEditCancel' | 'rootsEditPasswordHint' | 'rootsEditRenameHint' | 'rootsConfirmRemoveTitle' | 'rootsConfirmRemoveBody' | 'rootsConfirmRemoveAccept' | 'rootsConfirmRemoveCancel' | 'rootsConnection' | 'rootsConnectionPlaceholder' | 'rootsRemotePathLabel' | 'rootsIdLabel' | 'rootsHostLabel' | 'rootsUserLabel' | 'rootsAuthLabel' | 'rootsAuthKey' | 'rootsKeyPathLabel' | 'rootsPortLabel' | 'rootsBadgeLocal' | 'rootsBadgeRemote' | 'rootsErrorPathRequired' | 'rootsErrorRemotePath' | 'rootsErrorConnectionRequired' | 'rootsErrorLocalPath' | 'rootsErrorDuplicate' | 'rootsErrorIdRequired' | 'rootsErrorIdInvalid' | 'rootsErrorIdDuplicate' | 'rootsErrorHostRequired' | 'rootsErrorUserRequired' | 'rootsErrorPort' | 'rootsImportTitle' | 'rootsImportDescription' | 'rootsImportOpen' | 'rootsImportEmpty' | 'rootsImportRefresh' | 'rootsImportSelectAll' | 'rootsImportClear' | 'rootsImportAccept' | 'rootsImportCancel' | 'rootsImportBusy' | 'rootsImportDone' | 'rootsImportIdHint' | 'rootsImportSkippedTitle' | 'rootsImportSkipWildcard' | 'rootsImportSkipDuplicate' | 'rootsImportSkipTaken' | 'rootsImportSkipUnusableId' | 'rootsImportSkipNotImportable' | 'rootsImportSkipUnknown' | 'rootsImportHasKey' | 'rootsImportHasJump' | 'transferTitle' | 'transferDescription' | 'transferEmpty' | 'transferLoading' | 'transferRefresh' | 'transferClear' | 'transferFailedTitle' | 'transferDone' | 'transferCancelled' | 'transferUploadLabel' | 'transferDownloadLabel' | 'transferFailed' | 'transferFailedHttp' | 'transferFailedHost' | 'transferFailureLine' | 'transferErrorCodeUnknown' | 'remoteFilesHint' | 'rootsImportFailed' | 'rootsErrorKeyPathRequired' | 'rootsErrorPasswordRequired' | 'rootsNoticeUnavailable' | 'rootsNoticeWarnings' | 'rootsNoticeSavedConnection' | 'rootsNoticeSavedEdit' | 'flowTitle' | 'flowDescription' | 'flowLocalHeading' | 'flowLocalHint' | 'flowPickFailed' | 'flowRemoteHeading' | 'flowRemoteHint' | 'flowConfirmRemote' | 'flowCancel' | 'flowPending' | 'flowBusy' | 'flowNoConnections' | 'flowConnectionsLoading' | 'flowNoAnchor' | 'flowUnavailable' | 'browseHeading' | 'browseUp' | 'browseHome' | 'browseCurrent' | 'browseEmpty' | 'browseLoading' | 'browseRetry' | 'browseFailed' | 'browseUnavailable' | 'browseNeedConnection' | 'browseTruncated'
|
|
23
|
-
/** The remote world's connection list: reading, and failed-with-reason (audit 2026-11-01b ui P2-4). */
|
|
24
|
-
| 'browseConnectionsLoading' | 'browseConnectionsFailed' | 'browseKindFile' | 'browseKindSymlink' | 'browseKindOther' | 'browseLocalMode' | 'browsePickNative' | 'browseRemoteMode' | 'browseOpenLocal' | 'browseOpenRemote' | 'browseChoose' | 'sessionAddOpen' | 'sessionAddTitle' | 'sessionAddHint' | 'sessionAddWorkspace' | 'sessionAddWorkspaceUnknown' | 'sessionAddPick' | 'sessionBrowse' | 'sessionBrowseBack' | 'sessionRootsTitle' | 'sessionRootsEmpty'
|
|
25
|
-
/** The session entry's card while it reads its directory list (the empty state must not be shown then). */
|
|
26
|
-
| 'sessionRootsLoading' | 'sessionRootsHint' | 'sessionRootsFailed' | 'sessionAddDone' | 'sessionAddFailed' | 'sessionResultNoSelection' | 'sessionResultMissingCwd' | 'sessionResultMissingEntryId' | 'sessionResultHttpFailed' | 'sessionResultHostRefused' | 'sessionResultActionFailed' | 'sessionResultUnreachable' | 'sessionResultDeletedPermanent' | 'sessionResultDeleteNone' | 'sessionResultRecycled' | 'sessionResultWorkspaceEmpty' | 'sessionResultWorkspacePurged' | 'sessionResultWorkspacePurgedRefused' | 'sessionResultWorkspaceRecycled' | 'sessionResultWorkspaceRecycledRefused' | 'sessionResultRestored' | 'sessionResultPurged' | 'sessionResultSweepNone' | 'sessionResultSweepDone' | 'sessionWorkspaceUnknown' | 'connectionTestNoAnswer' | 'connectionTestFailed' | 'connectionTestRefused'
|
|
27
|
-
/** The probe ran out of the client's own deadline (no host sentence exists for it). */
|
|
28
|
-
| 'connectionTestTimeout' | 'transferSummaryRunning' | 'transferSummaryDone' | 'transferRemaining' | 'transferRemainingUnknown' | 'transferDurationUnknown' | 'rootsImportSummary' | 'rootsImportRefusedTitle' | 'rootsImportRefusedLine';
|
|
29
|
-
/** Merge this plugin's namespace into the shared locale map, so `t('over-ssh.…')` is typed. */
|
|
30
|
-
declare module '@deepseek-ai/dsh-client-ui-slots' {
|
|
31
|
-
interface LocaleNamespaceMap {
|
|
32
|
-
'over-ssh': OverSshKey;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
/** Every shipped locale's dictionary. Both `zh` and `en` are required by the runtime. */
|
|
36
|
-
export declare const OVER_SSH_DICTS: Record<'zh' | 'en', Record<OverSshKey, string>>;
|
|
37
|
-
/** Dictionary keys, so a completeness test never has to restate them. */
|
|
38
|
-
export declare function overSshKeys(): readonly OverSshKey[];
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Deferred copy: a dictionary key plus its parameters, and the one resolver for it.
|
|
3
|
-
*
|
|
4
|
-
* Why this is `core/` and not `roots/`: three modules need the same two lines — the roots panel model, the
|
|
5
|
-
* transfer panel and (since the audit) the session panel's result sentences — and two of them are **pure
|
|
6
|
-
* models** that must not import React. `roots/roots-panel-state.ts` used to own the resolver, which meant every
|
|
7
|
-
* pure model either imported a React-bearing module or (what actually happened) duplicated the six lines with a
|
|
8
|
-
* comment apologising for the duplication (`core/panel-model.ts`). One implementation, no React.
|
|
9
|
-
*
|
|
10
|
-
* The contract a dictionary keeps: a **missing** key renders as the key itself. That is deliberate — a missing
|
|
11
|
-
* translation must be visible in the UI, never silently blank (see `roots-panel-state.ts`'s original note).
|
|
12
|
-
*
|
|
13
|
-
* @module dsh-over-ssh/client/message
|
|
14
|
-
*/
|
|
15
|
-
/** A message is a key plus its parameters; a component resolves it against the active dictionary. */
|
|
16
|
-
export interface MessageKey {
|
|
17
|
-
/** The dictionary key (an entry of `OverSshKey` for this plugin's own copy). */
|
|
18
|
-
readonly key: string;
|
|
19
|
-
/** Values for the template's `{name}` placeholders. */
|
|
20
|
-
readonly params?: Readonly<Record<string, string | number>>;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Substitute `{name}` placeholders in one template.
|
|
24
|
-
*
|
|
25
|
-
* @param template - the dictionary string.
|
|
26
|
-
* @param params - the values to substitute; an absent value leaves the placeholder in place.
|
|
27
|
-
* @returns the rendered text.
|
|
28
|
-
*/
|
|
29
|
-
export declare function resolveTemplate(template: string, params: Readonly<Record<string, string | number>> | undefined): string;
|
|
30
|
-
/**
|
|
31
|
-
* Resolve one message against a dictionary.
|
|
32
|
-
*
|
|
33
|
-
* @param copy - the active locale's dictionary (a plain string map).
|
|
34
|
-
* @param message - the key plus optional parameters.
|
|
35
|
-
* @returns the rendered text, or `message.key` itself when the dictionary has no such key.
|
|
36
|
-
*/
|
|
37
|
-
export declare function resolveMessage(copy: Readonly<Record<string, string>>, message: MessageKey): string;
|
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The settings panel's pure logic and copy wiring.
|
|
3
|
-
*
|
|
4
|
-
* Two rules that keep a client component testable without a browser:
|
|
5
|
-
*
|
|
6
|
-
* - **no `ctx` in components** (the platform contract): everything a component shows arrives through props;
|
|
7
|
-
* - consequently all decision-making lives here, where Node can run it. The component is a template.
|
|
8
|
-
*
|
|
9
|
-
* The host data channel arrives as **untrusted wire data** (`unknown`), so the conversion below whitelists
|
|
10
|
-
* fields one by one: a leaked `keyPath` / `password` / `credentialKey` cannot reach the panel even if the host
|
|
11
|
-
* ever started sending one. That is the client half's half of the "no credentials over the channel" rule the
|
|
12
|
-
* host face also enforces.
|
|
13
|
-
*
|
|
14
|
-
* @module dsh-over-ssh/client/panel-model
|
|
15
|
-
*/
|
|
16
|
-
import type { OverSshKey } from './locale.ts';
|
|
17
|
-
/**
|
|
18
|
-
* Transports a connection row can name; `unknown` means "the host did not say".
|
|
19
|
-
*
|
|
20
|
-
* Declared once, in the roots model the wire parser lives in, and re-exported here so both halves of the panel
|
|
21
|
-
* vocabulary agree (a second identical union is how `unknown` ended up rendered as a raw English word).
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* How a connection reaches the host: `alias`/`ssh2` are Tier 1 (no agent), `agent` is Tier 2, `unknown` is
|
|
25
|
-
* "the host did not say".
|
|
26
|
-
*
|
|
27
|
-
* Defined **here**, in `core`, and re-exported by `roots/roots-panel.ts`, not the other way round: the
|
|
28
|
-
* dependency rule for this half is `core → roots → session → flows → entry`, and importing it from `roots`
|
|
29
|
-
* was a reverse edge that `tests/architecture.test.ts` caught (the panel model is what a caller without the
|
|
30
|
-
* editor imports, so it must not reach into a feature folder for a type it owns).
|
|
31
|
-
*/
|
|
32
|
-
export type ConnectionTransport = 'alias' | 'ssh2' | 'agent' | 'unknown';
|
|
33
|
-
/** One connection line: what the user needs to know about a single saved connection. */
|
|
34
|
-
export interface ConnectionRow {
|
|
35
|
-
/** Connection id (the profile key the user sees in `ssh://` paths). */
|
|
36
|
-
readonly id: string;
|
|
37
|
-
/** Host name as configured. */
|
|
38
|
-
readonly host: string;
|
|
39
|
-
/** Transport currently in force, or `unknown` before the first connection. */
|
|
40
|
-
readonly transport: ConnectionTransport;
|
|
41
|
-
/** Whether the current transport is a degraded substitute. */
|
|
42
|
-
readonly degraded: boolean;
|
|
43
|
-
/** Why: the degradation reason, else the decision's explanation, else "not connected yet". */
|
|
44
|
-
readonly note: string;
|
|
45
|
-
/**
|
|
46
|
-
* The row's badge text, already localized by whoever built the row.
|
|
47
|
-
*
|
|
48
|
-
* The template used to render {@link ConnectionRow.transport} raw, so every row carried a badge reading the
|
|
49
|
-
* English wire word `unknown` in the Chinese UI as well. `alias`/`ssh2`/`agent` are protocol names and stay as
|
|
50
|
-
* they are; only the "the host did not say" case needs a word, and it now arrives from the dictionary
|
|
51
|
-
* (audit 2026-11-01b ui P2-6).
|
|
52
|
-
*/
|
|
53
|
-
readonly transportLabel?: string;
|
|
54
|
-
/**
|
|
55
|
-
* The 「测试」 button's state for this row, already localized; absent when the panel has no tester.
|
|
56
|
-
*
|
|
57
|
-
* The template stays copy-free (`panel.ts` renders text it is handed), and the strings are rebuilt on every
|
|
58
|
-
* re-registration, so a locale change re-renders them without any subscription here.
|
|
59
|
-
*/
|
|
60
|
-
readonly test?: {
|
|
61
|
-
readonly status: 'idle' | 'running' | 'ok' | 'failed';
|
|
62
|
-
/** Button label: 「测试」, or 「测试中…」 while the probe runs. */
|
|
63
|
-
readonly label: string;
|
|
64
|
-
/** Result line; empty while running. */
|
|
65
|
-
readonly detail: string;
|
|
66
|
-
};
|
|
67
|
-
/**
|
|
68
|
-
* The row's own edit/delete labels, already localized; absent when the panel has no editor.
|
|
69
|
-
*
|
|
70
|
-
* Localized here rather than in the component for the same reason as {@link ConnectionRow.test}: the
|
|
71
|
-
* template renders strings it is handed and never looks up copy itself.
|
|
72
|
-
*/
|
|
73
|
-
readonly actions?: {
|
|
74
|
-
readonly editLabel: string;
|
|
75
|
-
readonly removeLabel: string;
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
/** What the panel renders. */
|
|
79
|
-
export interface PanelModel {
|
|
80
|
-
readonly title: string;
|
|
81
|
-
readonly description: string;
|
|
82
|
-
/** One line per connection; empty either before the host answered or when no connection is configured. */
|
|
83
|
-
readonly connections: readonly ConnectionRow[];
|
|
84
|
-
/** Heading of the connection list (it had none: the rows started right under the description). */
|
|
85
|
-
readonly connectionsHeading: string;
|
|
86
|
-
/** What an answered-but-empty list says (it used to render nothing at all). */
|
|
87
|
-
readonly connectionsEmpty: string;
|
|
88
|
-
/**
|
|
89
|
-
* Notice about the missing host data channel; empty once the host answered. An answered-but-empty list
|
|
90
|
-
* clears it too — "you configured no connection" and "the channel is not wired" are different facts, and
|
|
91
|
-
* the panel must not claim the second one when the first is true.
|
|
92
|
-
*/
|
|
93
|
-
readonly notice: string;
|
|
94
|
-
readonly closeLabel: string;
|
|
95
|
-
/**
|
|
96
|
-
* The confirmation card's text for deleting one connection, or absent when the panel cannot delete.
|
|
97
|
-
*
|
|
98
|
-
* A function of the row because the sentence names the connection **and** how many remote roots go with it
|
|
99
|
-
* - the fact that makes the confirmation necessary at all.
|
|
100
|
-
*/
|
|
101
|
-
readonly confirmRemove?: {
|
|
102
|
-
readonly title: string;
|
|
103
|
-
readonly cancelLabel: string;
|
|
104
|
-
readonly acceptLabel: string;
|
|
105
|
-
readonly body: (row: ConnectionRow, remoteRoots: number) => string;
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Convert the host's connection status payload into panel rows.
|
|
110
|
-
*
|
|
111
|
-
* Every field is read individually and type-checked; unknown fields (including any credential the host might
|
|
112
|
-
* ever add) are dropped rather than forwarded. A row without a usable `id` and `host` is skipped: an
|
|
113
|
-
* unidentifiable line is worse than a missing one.
|
|
114
|
-
*
|
|
115
|
-
* @param value - the remote call's value, exactly as it arrived over the wire.
|
|
116
|
-
* @returns the rows the panel may render, in the host's order.
|
|
117
|
-
*/
|
|
118
|
-
export declare function toConnectionRows(value: unknown): readonly ConnectionRow[];
|
|
119
|
-
/**
|
|
120
|
-
* Build the panel model from one locale's dictionary.
|
|
121
|
-
*
|
|
122
|
-
* @param copy - the dictionary the caller resolved (`zh` or `en`, chosen by the locale runtime).
|
|
123
|
-
* @param options - `rows` are the connection lines the host channel produced; **omitted** means the channel
|
|
124
|
-
* never answered, which is the only case that shows the notice.
|
|
125
|
-
* @returns everything the component needs, with no further lookups.
|
|
126
|
-
*/
|
|
127
|
-
export declare function buildPanelModel(copy: Record<OverSshKey, string>, options?: {
|
|
128
|
-
readonly rows?: readonly ConnectionRow[];
|
|
129
|
-
/**
|
|
130
|
-
* Whether the panel has an editor behind it. When true, rows carry their edit/delete labels and the model
|
|
131
|
-
* carries the removal confirmation; when false (the default) the page is read-only.
|
|
132
|
-
*/
|
|
133
|
-
readonly editable?: boolean;
|
|
134
|
-
}): PanelModel;
|