dsh-over-ssh 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -17
- package/agents/agent-manifest.json +6 -6
- package/agents/linux-amd64/dsh-over-ssh-agent +0 -0
- package/agents/linux-arm64/dsh-over-ssh-agent +0 -0
- package/lib/client.js +422 -337
- package/lib/file-references.js +92 -35
- package/lib/fs.js +295 -113
- package/lib/index.js +6912 -4330
- package/lib/terminal-wrapper.js +9 -4
- package/lib/types/src/config.d.ts +156 -9
- package/lib/types/src/file-references.d.ts +0 -1
- package/lib/types/src/host/app/backend.d.ts +44 -3
- 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 +23 -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/forward.d.ts +75 -0
- package/lib/types/src/host/infra/transport/ssh/ssh2-transport.d.ts +5 -1
- package/lib/types/src/host/infra/transport/transport-base.d.ts +0 -1
- package/lib/types/src/host/infra/transport/transport.d.ts +42 -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 +18 -3
- 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,266 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The **body-level portal**: the one place this plugin's cards are mounted outside the host's own layout.
|
|
3
|
-
*
|
|
4
|
-
* Why it has to exist, and why the previous "inline, narrow-first" plan was wrong: the
|
|
5
|
-
* `sidebar.workspaces.directoryFlow` hole renders **inside the sidebar's title row**, which is a flex row
|
|
6
|
-
* that clips its overflow. A card rendered in place there is a flex item, and a flex item is *shrunk*
|
|
7
|
-
* (`flex-shrink: 1`) before it ever overflows - `max-width: 100%` says nothing about shrinking, and the row
|
|
8
|
-
* does not wrap. The real screenshot after the last release showed exactly that: two or three vertical lines
|
|
9
|
-
* with a clipped "选择连接并填写". That is a fact about the real DOM, not a hypothesis, so the card has to
|
|
10
|
-
* leave the row: one container per mount, appended to the **injected** `document.body` while the interaction
|
|
11
|
-
* is open, removed again when it closes, and `position: fixed` inside it.
|
|
12
|
-
*
|
|
13
|
-
* Two rules this module keeps:
|
|
14
|
-
*
|
|
15
|
-
* - **The DOM is injected.** {@link createCardPortal} takes the document; nothing here touches `document` at
|
|
16
|
-
* module scope, so importing this module off-browser (Node's test runner, the host half) does nothing, and
|
|
17
|
-
* every branch is driven by a fake in the offline tests.
|
|
18
|
-
* - **One container per mount.** `mount()` and `unmount()` are idempotent: a re-render while the card is open
|
|
19
|
-
* must not append a second container, and a double close must not remove the same node twice.
|
|
20
|
-
*
|
|
21
|
-
* `createPortal` comes from `react-dom`, which is **not** a dependency of this repository but *is* a client
|
|
22
|
-
* baseline external: `PLATFORM_MODULES` in `packages/client/web/src/platform.ts` names it among the modules
|
|
23
|
-
* the shell seeds into the frozen module table, and the loader calls every client factory as
|
|
24
|
-
* `factory(require)` with a `require` that looks a specifier up in that seed table first
|
|
25
|
-
* (`ClientModuleSystem.makeRequire`). The lookup below therefore goes through that same `require` instead of
|
|
26
|
-
* a static import, for one concrete reason: a static `import 'react-dom'` is evaluated when Node's test
|
|
27
|
-
* runner loads this module, and `react-dom` is not installed here - every offline client test would fail at
|
|
28
|
-
* import time. The lookup is lazy, and a runtime that cannot supply `react-dom` renders the card in place
|
|
29
|
-
* (the pre-portal behaviour) instead of failing, with one warning so the degradation is visible in a log.
|
|
30
|
-
*
|
|
31
|
-
* @module dsh-over-ssh/client/portal
|
|
32
|
-
*/
|
|
33
|
-
import type { ReactNode } from 'react';
|
|
34
|
-
/** The part of `document` this module uses. Structural, so a fake document is a full citizen. */
|
|
35
|
-
export interface PortalDocument {
|
|
36
|
-
/** The element a card container is attached to while the card is open. */
|
|
37
|
-
readonly body?: PortalContainer | null | undefined;
|
|
38
|
-
/** Whatever held focus when the card opened; the card gives focus back to it when it closes. */
|
|
39
|
-
readonly activeElement?: FocusTarget | null | undefined;
|
|
40
|
-
createElement(tag: string): PortalElement;
|
|
41
|
-
/**
|
|
42
|
-
* Present on a real document; the shell hangs its global key listener here.
|
|
43
|
-
*
|
|
44
|
-
* `options` exists for one caller: the settings-hosted card traps Tab in the **capture** phase. The platform's
|
|
45
|
-
* own modal layer listens on `document` in the bubble phase and, seeing focus outside its panel, pulls it back
|
|
46
|
-
* — a bubble listener of ours would run *after* it (registration order) and could no longer tell which field
|
|
47
|
-
* Tab came from. Capture runs first, so the card keeps the key it owns.
|
|
48
|
-
*/
|
|
49
|
-
addEventListener?(type: 'keydown', listener: (event: PortalKeyEvent) => void, options?: boolean): void;
|
|
50
|
-
/** The other half of {@link addEventListener}. */
|
|
51
|
-
removeEventListener?(type: 'keydown', listener: (event: PortalKeyEvent) => void, options?: boolean): void;
|
|
52
|
-
}
|
|
53
|
-
/** One element the portal created. Only what the portal itself does with it is typed here. */
|
|
54
|
-
export interface PortalElement {
|
|
55
|
-
/** The class attribute; the port has already been given its vocabulary classes when it is attached. */
|
|
56
|
-
className: string;
|
|
57
|
-
/** Present on a real element; used to move focus into the card. */
|
|
58
|
-
querySelector?(selector: string): FocusTarget | null | undefined;
|
|
59
|
-
/** Present on a real element; used by the Tab trap to list the card's controls in order. */
|
|
60
|
-
querySelectorAll?(selector: string): ArrayLike<FocusTarget> | null | undefined;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* The part of a keyboard event the portal reads.
|
|
64
|
-
*
|
|
65
|
-
* Structural, like {@link KeyEventLike} in `./controls.ts`: the pure helpers are testable offline, and a real
|
|
66
|
-
* `KeyboardEvent` is assignable to it.
|
|
67
|
-
*/
|
|
68
|
-
export interface PortalKeyEvent {
|
|
69
|
-
/** The key name, e.g. `'Escape'` or `'Tab'`. */
|
|
70
|
-
readonly key?: string;
|
|
71
|
-
/** True while Shift is held: the Tab trap walks backwards. */
|
|
72
|
-
readonly shiftKey?: boolean;
|
|
73
|
-
/** Cancel the browser's own focus move when the trap takes over. */
|
|
74
|
-
preventDefault?(): void;
|
|
75
|
-
/** Stop the event before a modal layer behind the card sees it (see {@link trapCardTab}). */
|
|
76
|
-
stopPropagation?(): void;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* The **one** shell every confirmation card is portalled into.
|
|
80
|
-
*
|
|
81
|
-
* A confirmation is the same object wherever it is raised: a sentence naming what will be destroyed, and two
|
|
82
|
-
* buttons. Three surfaces used to compose that object three ways (audit 2026-11-01b ui P2-8 / task-12 B1); this
|
|
83
|
-
* constant is the whole fix, together with {@link CONFIRM_BACKDROP_CLASS}.
|
|
84
|
-
*/
|
|
85
|
-
export declare const CONFIRM_PORTAL_CLASS: string;
|
|
86
|
-
/**
|
|
87
|
-
* The confirmation's click sink.
|
|
88
|
-
*
|
|
89
|
-
* It uses the settings tier (1001) because the settings panel is one of the three raisers and its modal uses
|
|
90
|
-
* 900/1001; the overlay is above everything and sits at the same tier, so one constant is right for both.
|
|
91
|
-
*/
|
|
92
|
-
export declare const CONFIRM_BACKDROP_CLASS: string;
|
|
93
|
-
/** The two operations the portal needs from `document.body`. */
|
|
94
|
-
export interface PortalContainer {
|
|
95
|
-
appendChild(node: PortalElement): unknown;
|
|
96
|
-
removeChild(node: PortalElement): unknown;
|
|
97
|
-
}
|
|
98
|
-
/** Anything that can take focus; `document.activeElement` is one. */
|
|
99
|
-
export interface FocusTarget {
|
|
100
|
-
focus?(): void;
|
|
101
|
-
readonly isConnected?: boolean;
|
|
102
|
-
}
|
|
103
|
-
/** Render a card somewhere else. The browser passes a function built on `react-dom`'s `createPortal`. */
|
|
104
|
-
export type PortalRenderer = (children: ReactNode, container: unknown) => ReactNode;
|
|
105
|
-
/**
|
|
106
|
-
* What a template needs to put its card in a portal: the container element (created from the injected
|
|
107
|
-
* document) and the renderer that fills it.
|
|
108
|
-
*
|
|
109
|
-
* The template never creates either of them - it only decides whether it has one. That keeps the templates
|
|
110
|
-
* pure element trees the offline tests walk, and makes "the card is not in the host container" an assertion
|
|
111
|
-
* about an injected fake rather than about React.
|
|
112
|
-
*/
|
|
113
|
-
export interface CardPortalTarget {
|
|
114
|
-
/** The body-level element the card is rendered into. */
|
|
115
|
-
readonly container: unknown;
|
|
116
|
-
/** Render the card into {@link container}. */
|
|
117
|
-
readonly render: PortalRenderer;
|
|
118
|
-
}
|
|
119
|
-
/** One mount's body-level container, plus its attach/detach lifecycle. */
|
|
120
|
-
export interface CardPortal extends CardPortalTarget {
|
|
121
|
-
/**
|
|
122
|
-
* The click sink that sits **under** the card and counts as "the page behind the dialog".
|
|
123
|
-
*
|
|
124
|
-
* `undefined` when the caller asked for no backdrop, when there is no document, or when the runtime cannot
|
|
125
|
-
* portal at all (an in-place card has no modal geometry to protect). See {@link CreateCardPortalOptions.backdropClassName}.
|
|
126
|
-
*/
|
|
127
|
-
readonly backdrop: PortalElement | undefined;
|
|
128
|
-
/** True while the container is attached to `document.body`. */
|
|
129
|
-
readonly mounted: boolean;
|
|
130
|
-
/** Attach the container; a second call while attached does nothing. */
|
|
131
|
-
mount(): void;
|
|
132
|
-
/** Detach the container; a second call while detached does nothing. */
|
|
133
|
-
unmount(): void;
|
|
134
|
-
/** Move focus to the card's first control. `false` when there is nothing focusable (or no DOM). */
|
|
135
|
-
focusCard(): boolean;
|
|
136
|
-
}
|
|
137
|
-
/** What {@link createCardPortal} needs: the injected document, the container's classes and the renderer. */
|
|
138
|
-
export interface CreateCardPortalOptions {
|
|
139
|
-
/** The page's `document`, or `undefined` off-browser, where the whole portal is a no-op. */
|
|
140
|
-
readonly document: PortalDocument | undefined;
|
|
141
|
-
/** Classes of the body-level container (`CLASS.portal` plus one placement class). */
|
|
142
|
-
readonly className: string;
|
|
143
|
-
/** How the card is rendered into the container; the shells pass {@link renderInPortal}. */
|
|
144
|
-
readonly render: PortalRenderer;
|
|
145
|
-
/**
|
|
146
|
-
* Classes of the **backdrop**: the element this portal inserts under the card to swallow pointer input.
|
|
147
|
-
*
|
|
148
|
-
* Why it exists at all: the card claims `aria-modal="true"` and traps Tab, but the page behind it stayed
|
|
149
|
-
* *clickable* — the portal only ever created the card's own container, which hugs the card, so everywhere else
|
|
150
|
-
* the pointer reached the surface underneath. With the connection editor open that meant a click could land on
|
|
151
|
-
* the 「保存」 of the form behind it (and, before the drafts were split, submit the dialog's values as a new
|
|
152
|
-
* connection). A backdrop is the pointer half of the modal promise; the Tab trap is the keyboard half
|
|
153
|
-
* (audit 2026-11-01b ui P2-1).
|
|
154
|
-
*
|
|
155
|
-
* Optional so a caller that renders a **non-modal** card keeps the old behaviour, and so the offline portal
|
|
156
|
-
* tests can keep asserting the container lifecycle alone.
|
|
157
|
-
*/
|
|
158
|
-
readonly backdropClassName?: string;
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* The selector of the first thing in a card that can take focus.
|
|
162
|
-
*
|
|
163
|
-
* Disabled controls are excluded, and so is a `tabindex="-1"` element (a programmatic focus target, not a
|
|
164
|
-
* user one): the point of the initial focus is that the next Tab goes somewhere sensible.
|
|
165
|
-
*/
|
|
166
|
-
export declare const CARD_FOCUS_SELECTOR = "button:not([disabled]), input:not([disabled]), select:not([disabled]), a[href], [tabindex]:not([tabindex=\"-1\"])";
|
|
167
|
-
/**
|
|
168
|
-
* Create one mount's card portal.
|
|
169
|
-
*
|
|
170
|
-
* The container is created **eagerly** (a React portal needs its container during the commit that renders the
|
|
171
|
-
* card) but attached **lazily**: nothing is in the body until `mount()` runs, which the shell does from a
|
|
172
|
-
* layout effect on the open edge, so a closed surface costs no DOM at all.
|
|
173
|
-
*
|
|
174
|
-
* @param options - the injected document, the container's classes and the card renderer.
|
|
175
|
-
* @returns the portal handle; every method is safe with no document and safe to call twice.
|
|
176
|
-
*/
|
|
177
|
-
export declare function createCardPortal(options: CreateCardPortalOptions): CardPortal;
|
|
178
|
-
/**
|
|
179
|
-
* Move focus to the first control inside a container.
|
|
180
|
-
*
|
|
181
|
-
* @param container - the card's container, or `undefined` when there is no DOM.
|
|
182
|
-
* @returns whether something actually took focus.
|
|
183
|
-
*/
|
|
184
|
-
export declare function focusFirstControl(container: PortalElement | undefined): boolean;
|
|
185
|
-
/**
|
|
186
|
-
* Every control inside the card that Tab can reach, in DOM order.
|
|
187
|
-
*
|
|
188
|
-
* @param container - the card's container, or `undefined` when there is no DOM.
|
|
189
|
-
* @returns the focusable elements; `[]` when the container or `querySelectorAll` is absent.
|
|
190
|
-
*/
|
|
191
|
-
export declare function focusableWithin(container: PortalElement | undefined): readonly FocusTarget[];
|
|
192
|
-
/**
|
|
193
|
-
* Keep Tab inside the open card.
|
|
194
|
-
*
|
|
195
|
-
* A dialog that lets Tab walk into the page behind it is a dialog the keyboard user can leave without
|
|
196
|
-
* dismissing; trapping the two ends is the minimum the modal promise requires. `active` is passed in (rather
|
|
197
|
-
* than read from the document) so the rule is a pure function of its arguments.
|
|
198
|
-
*
|
|
199
|
-
* @param event - the keydown event, structurally.
|
|
200
|
-
* @param container - the card's container.
|
|
201
|
-
* @param active - the element that currently holds focus.
|
|
202
|
-
* @returns whether the trap handled the key (and already called `preventDefault`).
|
|
203
|
-
*/
|
|
204
|
-
export declare function trapTab(event: {
|
|
205
|
-
readonly shiftKey?: boolean;
|
|
206
|
-
preventDefault?(): void;
|
|
207
|
-
}, container: PortalElement | undefined, active: FocusTarget | undefined): boolean;
|
|
208
|
-
/**
|
|
209
|
-
* Keep Tab inside a card that is hosted **inside another modal**.
|
|
210
|
-
*
|
|
211
|
-
* The settings page is a platform modal with its own focus trap (`useModalLayer`): it listens for Tab on
|
|
212
|
-
* `document` and, when focus is not inside its panel, moves focus to that panel's first control. This card is
|
|
213
|
-
* deliberately *outside* that panel (it is portalled to the body above the modal layers, audit 0088), so without
|
|
214
|
-
* this the platform would pull the caret out of the dialog on the first Tab.
|
|
215
|
-
*
|
|
216
|
-
* Two things have to happen, in this order:
|
|
217
|
-
*
|
|
218
|
-
* 1. `stopPropagation` — the platform's listener is on `document` in the **bubble** phase, so the card's own
|
|
219
|
-
* listener must be registered in the capture phase ({@link PortalDocument.addEventListener}'s `options`) and
|
|
220
|
-
* consume the event before it bubbles back up;
|
|
221
|
-
* 2. delegate to {@link trapTab} for the wrap at both ends. **In the middle it returns `false` and does not
|
|
222
|
-
* preventDefault** — the browser's own Tab then walks the card's fields in order, which is exactly right.
|
|
223
|
-
*
|
|
224
|
-
* @param event - the keydown event, structurally.
|
|
225
|
-
* @param container - the card's container.
|
|
226
|
-
* @param active - the element that currently holds focus.
|
|
227
|
-
* @returns whether Tab was consumed (the trap already called `preventDefault`).
|
|
228
|
-
*/
|
|
229
|
-
export declare function trapCardTab(event: PortalKeyEvent, container: PortalElement | undefined, active: FocusTarget | undefined): boolean;
|
|
230
|
-
/**
|
|
231
|
-
* Remember what held focus before the card opened.
|
|
232
|
-
*
|
|
233
|
-
* @param doc - the page's `document`, or `undefined` off-browser.
|
|
234
|
-
* @returns the focused element, or `undefined` when nothing was focused.
|
|
235
|
-
*/
|
|
236
|
-
export declare function captureFocusOrigin(doc: PortalDocument | undefined): FocusTarget | undefined;
|
|
237
|
-
/**
|
|
238
|
-
* Give focus back to the element {@link captureFocusOrigin} remembered.
|
|
239
|
-
*
|
|
240
|
-
* @param origin - the remembered element.
|
|
241
|
-
* @returns whether focus was handed back; `false` when the trigger is gone (the owner re-rendered it away).
|
|
242
|
-
*/
|
|
243
|
-
export declare function restoreFocusOrigin(origin: FocusTarget | undefined): boolean;
|
|
244
|
-
/**
|
|
245
|
-
* True when a card can actually be portalled here: this runtime supplies `react-dom`.
|
|
246
|
-
*
|
|
247
|
-
* Without it the shell must render the card in place and skip the container lifecycle entirely (an empty
|
|
248
|
-
* body-level node would just be appended and removed for nothing).
|
|
249
|
-
*
|
|
250
|
-
* @returns whether {@link renderInPortal} has a `createPortal` to work with.
|
|
251
|
-
*/
|
|
252
|
-
export declare function canRenderInPortal(): boolean;
|
|
253
|
-
/**
|
|
254
|
-
* Render a card into the portal container, with `react-dom`'s `createPortal`.
|
|
255
|
-
*
|
|
256
|
-
* @param children - the card element the template built.
|
|
257
|
-
* @param container - the body-level element from {@link createCardPortal}.
|
|
258
|
-
* @returns the portal element, or `children` unchanged when this runtime supplies no `react-dom`.
|
|
259
|
-
*/
|
|
260
|
-
export declare function renderInPortal(children: ReactNode, container: unknown): ReactNode;
|
|
261
|
-
/**
|
|
262
|
-
* The page's `document`, when this module runs in a browser.
|
|
263
|
-
*
|
|
264
|
-
* @returns the structural document view, or `undefined` off-browser.
|
|
265
|
-
*/
|
|
266
|
-
export declare function browserPortalDocument(): PortalDocument | undefined;
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The client half's host data channel: mount the `sshdev` namespace and read connection status.
|
|
3
|
-
*
|
|
4
|
-
* Three facts decide the shape of this module, all read from 0.2.0-rc.2 (see
|
|
5
|
-
* [`doc/02-platform/remote-data-channel.md`](../../doc/02-platform/remote-data-channel.md)):
|
|
6
|
-
*
|
|
7
|
-
* 1. **The namespace is mounted by us, not by `api-remotes`.** That core assembly lists its packages
|
|
8
|
-
* explicitly, so a third-party plugin mounts its own `TypertRemoteContribution` through
|
|
9
|
-
* `ctx.remote.$mount`.
|
|
10
|
-
* 2. **`remote.<ns>` must NOT be in the plugin's `inject`.** We create the namespace ourselves inside
|
|
11
|
-
* `apply`; declaring it as a dependency would wait forever for a service only we can provide.
|
|
12
|
-
* 3. **The descriptors are hand-written and zero-argument.** `$mount` rejects any descriptor whose
|
|
13
|
-
* parameter/uplink/Context codec is not strict, and only the Typert build pipeline produces strict
|
|
14
|
-
* codecs. A method with **no parameters** triggers none of those checks, which is why the slice stops
|
|
15
|
-
* at one zero-argument read; anything with arguments needs generated `lib/typert.remote-client.js`.
|
|
16
|
-
*
|
|
17
|
-
* The wire contract (`sshdev/connections` → an array of `{id, host, transport, degraded, note}`) is mirrored
|
|
18
|
-
* in `src/host/integration/remote-face.ts`; the two halves are separate tsconfig programs and cannot import
|
|
19
|
-
* each other, so the constants below are a hand-synchronized pair.
|
|
20
|
-
*
|
|
21
|
-
* @module dsh-over-ssh/client/remote
|
|
22
|
-
*/
|
|
23
|
-
import type { Context } from '@deepseek-ai/cordis';
|
|
24
|
-
import type { InvocationDescriptor, TypertDisposer, TypertRemoteContribution } from '@deepseek-ai/dsh-typert-protocol';
|
|
25
|
-
import { type ConnectionRow } from './panel-model.ts';
|
|
26
|
-
/** Wire namespace; the host face declares the same string. */
|
|
27
|
-
export declare const SSH_DEV_REMOTE_NAMESPACE = "sshdev";
|
|
28
|
-
/** Endpoint method name. */
|
|
29
|
-
export declare const SSH_DEV_REMOTE_METHOD = "connections";
|
|
30
|
-
/** Host Cordis service key the descriptor dispatches to (must match the host face's service name). */
|
|
31
|
-
export declare const SSH_DEV_REMOTE_SERVICE = "sshDevRemote";
|
|
32
|
-
/** Contribution identity; `$mount` rejects a repeat of the same package name. */
|
|
33
|
-
export declare const SSH_DEV_REMOTE_PACKAGE = "dsh-over-ssh";
|
|
34
|
-
/**
|
|
35
|
-
* The one descriptor this slice mounts.
|
|
36
|
-
*
|
|
37
|
-
* `result: { mode: 'src-json' }` is the honest declaration: no strict result codec exists for this endpoint in
|
|
38
|
-
* this repository, and the Client Remote only consults `result.decode` when the mode is `strict`
|
|
39
|
-
* (`packages/api/gateway/src/client/index.ts:449-451`), so the raw JSON value passes through and is narrowed
|
|
40
|
-
* by {@link toConnectionRows} instead.
|
|
41
|
-
*/
|
|
42
|
-
export declare const SSH_DEV_CONNECTIONS_DESCRIPTOR: InvocationDescriptor;
|
|
43
|
-
/** The mountable contribution. */
|
|
44
|
-
export declare const SSH_DEV_REMOTE_CONTRIBUTION: TypertRemoteContribution;
|
|
45
|
-
/**
|
|
46
|
-
* Mount the `sshdev` namespace in the caller's fiber.
|
|
47
|
-
*
|
|
48
|
-
* The returned disposer withdraws the namespace and its concrete method; `$mount` also registers everything
|
|
49
|
-
* as an effect of the calling context, so unloading the plugin tears the channel down even if the caller
|
|
50
|
-
* never keeps the disposer.
|
|
51
|
-
*
|
|
52
|
-
* @param ctx - the plugin's client context (needs `remote`).
|
|
53
|
-
* @returns the mount disposer.
|
|
54
|
-
*/
|
|
55
|
-
export declare function mountConnectionChannel(ctx: Context): Promise<TypertDisposer>;
|
|
56
|
-
/**
|
|
57
|
-
* Read the host's connection status once.
|
|
58
|
-
*
|
|
59
|
-
* Deliberately non-throwing: a channel that is absent, unmounted, offline, answered with a failure, or that
|
|
60
|
-
* rejects outright yields `undefined`, and the panel keeps its "no host data" notice.
|
|
61
|
-
*
|
|
62
|
-
* @param ctx - the plugin's client context; the namespace must already be mounted.
|
|
63
|
-
* @returns the panel rows, or `undefined` when no usable answer arrived.
|
|
64
|
-
*/
|
|
65
|
-
export declare function fetchConnectionRows(ctx: Context): Promise<readonly ConnectionRow[] | undefined>;
|
|
66
|
-
/** Where {@link openConnectionChannel} reports its outcome. */
|
|
67
|
-
export interface ConnectionChannelObserver {
|
|
68
|
-
/** Called once with the first rows the host answered — including an empty list (no connection configured). */
|
|
69
|
-
readonly onRows: (rows: readonly ConnectionRow[]) => void;
|
|
70
|
-
/** Called when the channel could not be mounted or answered; the panel keeps its notice. */
|
|
71
|
-
readonly onUnavailable?: (reason: unknown) => void;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Mount the channel, read it once, and hand the rows to `observer.onRows`.
|
|
75
|
-
*
|
|
76
|
-
* One read, no polling and no subscription: the panel is a snapshot of the moment the settings page (or the
|
|
77
|
-
* plugin) came up. Live refresh needs an observable source on the inject face — see the limitations section of
|
|
78
|
-
* `doc/02-platform/remote-data-channel.md`.
|
|
79
|
-
*
|
|
80
|
-
* @param ctx - the plugin's client context (needs `remote`).
|
|
81
|
-
* @param observer - row sink plus an optional unavailable-reason sink.
|
|
82
|
-
* @returns the mount disposer, or `undefined` when the channel never mounted.
|
|
83
|
-
*/
|
|
84
|
-
export declare function openConnectionChannel(ctx: Context, observer: ConnectionChannelObserver): Promise<TypertDisposer | undefined>;
|