@lemoncat7/dsh-ssh 1.1.8 → 1.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/ARCHITECTURE.md +62 -0
- package/README.md +25 -17
- package/lib/activity-panel.d.ts +17 -0
- package/lib/activity-panel.d.ts.map +1 -0
- package/lib/activity-panel.js +160 -0
- package/lib/activity-panel.js.map +1 -0
- package/lib/adaptive-workspace.d.ts.map +1 -1
- package/lib/adaptive-workspace.js +19 -4
- package/lib/adaptive-workspace.js.map +1 -1
- package/lib/api.js +93 -6
- package/lib/api.js.map +1 -1
- package/lib/client-api.d.ts +20 -0
- package/lib/client-api.d.ts.map +1 -1
- package/lib/client-api.js +13 -0
- package/lib/client-api.js.map +1 -1
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +3708 -21057
- package/lib/client.js.map +4 -4
- package/lib/domain.d.ts +17 -1
- package/lib/domain.d.ts.map +1 -1
- package/lib/domain.js +15 -0
- package/lib/domain.js.map +1 -1
- package/lib/profile-editor.d.ts +16 -0
- package/lib/profile-editor.d.ts.map +1 -0
- package/lib/profile-editor.js +143 -0
- package/lib/profile-editor.js.map +1 -0
- package/lib/project-session-dialog.d.ts +15 -0
- package/lib/project-session-dialog.d.ts.map +1 -0
- package/lib/project-session-dialog.js +60 -0
- package/lib/project-session-dialog.js.map +1 -0
- package/lib/remote-workspace-tree.d.ts +28 -0
- package/lib/remote-workspace-tree.d.ts.map +1 -0
- package/lib/remote-workspace-tree.js +127 -0
- package/lib/remote-workspace-tree.js.map +1 -0
- package/lib/resizable-split.d.ts +10 -0
- package/lib/resizable-split.d.ts.map +1 -0
- package/lib/resizable-split.js +115 -0
- package/lib/resizable-split.js.map +1 -0
- package/lib/session-access-channel.d.ts +6 -0
- package/lib/session-access-channel.d.ts.map +1 -0
- package/lib/session-access-channel.js +10 -0
- package/lib/session-access-channel.js.map +1 -0
- package/lib/session-access.d.ts +15 -0
- package/lib/session-access.d.ts.map +1 -0
- package/lib/session-access.js +99 -0
- package/lib/session-access.js.map +1 -0
- package/lib/sftp-client.d.ts +5 -1
- package/lib/sftp-client.d.ts.map +1 -1
- package/lib/sftp-client.js +85 -31
- package/lib/sftp-client.js.map +1 -1
- package/lib/store.d.ts +3 -1
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +35 -3
- package/lib/store.js.map +1 -1
- package/lib/terminal.d.ts +2 -0
- package/lib/terminal.d.ts.map +1 -1
- package/lib/terminal.js +10 -2
- package/lib/terminal.js.map +1 -1
- package/lib/ui-components.d.ts +25 -0
- package/lib/ui-components.d.ts.map +1 -0
- package/lib/ui-components.js +27 -0
- package/lib/ui-components.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useMemo, useState } from 'react';
|
|
3
|
+
import { IconChevronDownOutline14, IconEditOutline16, IconFolderClose16, IconFolderOpenOutline16, IconPlusOutline16, IconTrashOutline16, } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
|
+
import { createRemoteProject, deleteRemoteProject, loadRemoteProjects, updateRemoteProject, } from './client-api.js';
|
|
5
|
+
import { ProjectSessionDialog } from './project-session-dialog.js';
|
|
6
|
+
import { Dialog } from './ui-components.js';
|
|
7
|
+
export function RemoteWorkspaceTree(props) {
|
|
8
|
+
const [query, setQuery] = useState('');
|
|
9
|
+
const [expanded, setExpanded] = useState(() => new Set());
|
|
10
|
+
const [projects, setProjects] = useState({});
|
|
11
|
+
const [loadingProfile, setLoadingProfile] = useState();
|
|
12
|
+
const [editing, setEditing] = useState();
|
|
13
|
+
const [creatingSession, setCreatingSession] = useState();
|
|
14
|
+
const [error, setError] = useState();
|
|
15
|
+
const normalized = query.trim().toLocaleLowerCase();
|
|
16
|
+
const groups = useMemo(() => groupProfiles(props.profiles.filter(profile => searchText(profile).includes(normalized))), [normalized, props.profiles]);
|
|
17
|
+
const refreshProjects = async (profileId) => {
|
|
18
|
+
setLoadingProfile(profileId);
|
|
19
|
+
try {
|
|
20
|
+
const next = await loadRemoteProjects(profileId);
|
|
21
|
+
setProjects(current => ({ ...current, [profileId]: next }));
|
|
22
|
+
setError(undefined);
|
|
23
|
+
}
|
|
24
|
+
catch (reason) {
|
|
25
|
+
setError(message(reason));
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
setLoadingProfile(current => current === profileId ? undefined : current);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const toggleExpanded = (profileId) => {
|
|
32
|
+
setExpanded(current => {
|
|
33
|
+
const next = new Set(current);
|
|
34
|
+
if (next.has(profileId))
|
|
35
|
+
next.delete(profileId);
|
|
36
|
+
else
|
|
37
|
+
next.add(profileId);
|
|
38
|
+
return next;
|
|
39
|
+
});
|
|
40
|
+
if (projects[profileId] === undefined)
|
|
41
|
+
void refreshProjects(profileId);
|
|
42
|
+
};
|
|
43
|
+
const toggleProfile = (profileId) => {
|
|
44
|
+
const current = props.access?.profileIds ?? [];
|
|
45
|
+
const enabled = current.includes(profileId);
|
|
46
|
+
props.onProfiles(enabled ? current.filter(id => id !== profileId) : [...current, profileId]);
|
|
47
|
+
if (enabled)
|
|
48
|
+
props.onDirectory(profileId, undefined);
|
|
49
|
+
};
|
|
50
|
+
return _jsxs("aside", { className: "dsh-ssh-remote-tree", children: [_jsxs("header", { className: "dsh-ssh-tree-header", children: [_jsxs("span", { children: [_jsx("strong", { children: "\u4E3B\u673A\u4E0E\u9879\u76EE" }), _jsxs("small", { children: [props.profiles.length, " \u53F0\u4E3B\u673A"] })] }), _jsx("button", { type: "button", className: "dsh-ssh-icon-button", onClick: props.onNewProfile, "aria-label": "\u65B0\u5EFA\u8FDE\u63A5", title: "\u65B0\u5EFA\u8FDE\u63A5", children: _jsx(IconPlusOutline16, { size: 16 }) })] }), _jsxs("label", { className: "dsh-ssh-search", children: [_jsx("span", { className: "sr-only", children: "\u641C\u7D22\u4E3B\u673A" }), _jsx("input", { value: query, onChange: event => setQuery(event.target.value), placeholder: "\u641C\u7D22\u4E3B\u673A\u3001\u5206\u7EC4\u3001\u6807\u7B7E\u2026" })] }), _jsxs("div", { className: "dsh-ssh-tree-scroll dsh-ssh-scroll-surface", children: [groups.map(group => _jsxs("section", { className: "dsh-ssh-tree-group", children: [_jsxs("h3", { children: [_jsx("span", { children: group.name }), _jsx("small", { children: group.profiles.length })] }), group.profiles.map(profile => {
|
|
51
|
+
const open = expanded.has(profile.id);
|
|
52
|
+
const enabled = props.access?.profileIds.includes(profile.id) === true;
|
|
53
|
+
const children = projects[profile.id] ?? [];
|
|
54
|
+
const active = props.selected?.profileId === profile.id && props.selected.projectId === undefined;
|
|
55
|
+
return _jsxs("div", { className: "dsh-ssh-tree-host", "data-open": open, children: [_jsxs("div", { className: `dsh-ssh-tree-host-row${enabled ? ' is-authorized' : ''}${active ? ' is-active' : ''}`, children: [_jsx("button", { type: "button", className: "dsh-ssh-tree-disclosure", "aria-label": `${open ? '收起' : '展开'} ${profile.name} 的固定目录`, "aria-expanded": open, onClick: () => toggleExpanded(profile.id), children: _jsx("span", { className: "dsh-ssh-tree-chevron", children: _jsx(IconChevronDownOutline14, { size: 12 }) }) }), _jsxs("button", { type: "button", className: "dsh-ssh-tree-host-main", "aria-pressed": props.access === null ? undefined : enabled, title: props.access === null ? '当前没有可授权的 DSH 会话' : enabled ? '撤销当前会话访问' : '允许当前会话访问', onClick: () => {
|
|
56
|
+
if (props.access !== null && !props.accessLoading)
|
|
57
|
+
toggleProfile(profile.id);
|
|
58
|
+
props.onSelect({ profileId: profile.id, path: '~' });
|
|
59
|
+
}, children: [_jsx("span", { className: "dsh-ssh-host-monogram", children: profile.name.slice(0, 1).toUpperCase() }), _jsxs("span", { children: [_jsx("strong", { children: profile.name }), _jsxs("small", { children: [profile.username, "@", profile.host] })] })] }), enabled && _jsx("span", { className: "dsh-ssh-mount-state", children: "\u5DF2\u6302\u8F7D" }), _jsx("button", { type: "button", className: "dsh-ssh-tree-add", "aria-label": `为 ${profile.name} 添加固定目录`, title: "\u6DFB\u52A0\u56FA\u5B9A\u76EE\u5F55", onClick: () => setEditing({ profile }), children: _jsx(IconPlusOutline16, { size: 14 }) })] }), open && _jsx("div", { className: "dsh-ssh-tree-branches", children: loadingProfile === profile.id && projects[profile.id] === undefined ? _jsx("p", { className: "dsh-ssh-tree-state", children: "\u6B63\u5728\u8BFB\u53D6\u56FA\u5B9A\u76EE\u5F55\u2026" })
|
|
60
|
+
: children.length === 0 ? _jsxs("button", { type: "button", className: "dsh-ssh-tree-empty", onClick: () => setEditing({ profile }), children: [_jsx(IconPlusOutline16, { size: 13 }), "\u6DFB\u52A0\u9879\u76EE\u76EE\u5F55"] })
|
|
61
|
+
: children.map(project => {
|
|
62
|
+
const projectActive = props.selected?.projectId === project.id;
|
|
63
|
+
const bound = props.access?.workingProjectIds[profile.id] === project.id;
|
|
64
|
+
return _jsx("div", { className: "dsh-ssh-tree-project", children: _jsxs("div", { className: `dsh-ssh-tree-project-row${projectActive ? ' is-active' : ''}${bound ? ' is-bound' : ''}`, children: [_jsxs("button", { type: "button", className: "dsh-ssh-tree-project-main", "aria-pressed": props.access === null ? undefined : bound, title: props.access === null ? '当前没有可固定目录的 DSH 会话' : !enabled ? '请先挂载该主机' : bound ? '取消当前会话的固定目录' : '固定为当前会话目录', onClick: () => {
|
|
65
|
+
if (enabled && !props.accessLoading && !props.accessSaving) {
|
|
66
|
+
props.onDirectory(profile.id, bound ? undefined : project.path, bound ? undefined : project.id);
|
|
67
|
+
props.onSelect(bound ? { profileId: profile.id, path: '~' } : { profileId: profile.id, path: project.path, projectId: project.id });
|
|
68
|
+
}
|
|
69
|
+
}, children: [_jsx("span", { children: bound ? _jsx(IconFolderOpenOutline16, { size: 15 }) : _jsx(IconFolderClose16, { size: 15 }) }), _jsxs("span", { children: [_jsx("strong", { children: project.name }), _jsx("small", { children: project.path }), bound && _jsx("em", { children: "\u5F53\u524D\u4F1A\u8BDD\u5DF2\u56FA\u5B9A\u8BE5\u8DEF\u5F84" })] })] }), _jsx("button", { type: "button", className: "dsh-ssh-tree-project-new", "aria-label": `在 ${project.name} 新建会话`, title: "\u65B0\u5EFA\u4F1A\u8BDD", onClick: event => { setError(undefined); setCreatingSession({ profile, project, returnFocus: event.currentTarget }); }, children: _jsx(IconPlusOutline16, { size: 13 }) }), _jsx("button", { type: "button", className: "dsh-ssh-tree-project-edit", "aria-label": `编辑 ${project.name}`, title: "\u7F16\u8F91\u56FA\u5B9A\u76EE\u5F55", onClick: () => setEditing({ profile, project }), children: _jsx(IconEditOutline16, { size: 13 }) })] }) }, project.id);
|
|
70
|
+
}) })] }, profile.id);
|
|
71
|
+
})] }, group.name)), groups.length === 0 && _jsx("p", { className: "dsh-ssh-tree-no-results", children: props.profiles.length === 0 ? '还没有 SSH 主机' : '没有匹配的主机' })] }), _jsx(SessionAccessFooter, { access: props.access, loading: props.accessLoading, saving: props.accessSaving, error: props.accessError ?? error, onPermission: props.onPermission, onApproval: props.onApproval }), editing !== undefined && _jsx(RemoteProjectDialog, { profile: editing.profile, project: editing.project, onClose: () => setEditing(undefined), onSaved: async () => { const profileId = editing.profile.id; setEditing(undefined); await refreshProjects(profileId); } }), creatingSession !== undefined && _jsx(ProjectSessionDialog, { ...creatingSession, workspaces: props.workspaces, currentWorkspaceId: props.currentWorkspaceId, recentWorkspaceId: props.recentWorkspaceId, onClose: () => setCreatingSession(undefined), onCreate: props.onCreateSession })] });
|
|
72
|
+
}
|
|
73
|
+
function SessionAccessFooter({ access, loading, saving, error, onPermission, onApproval }) {
|
|
74
|
+
return _jsxs("footer", { className: "dsh-ssh-access-footer", children: [_jsxs("div", { className: "dsh-ssh-access-heading", children: [_jsxs("span", { children: [_jsx("strong", { children: "\u5F53\u524D\u4F1A\u8BDD\u6743\u9650" }), _jsxs("small", { children: [access?.profileIds.length ?? 0, " \u53F0\u4E3B\u673A\u53EF\u7528"] })] }), _jsx("em", { children: loading ? '读取中' : saving ? '保存中' : '已同步' })] }), _jsxs("div", { className: "dsh-ssh-access-segments", "aria-label": "SSH \u6743\u9650", children: [_jsx("button", { type: "button", className: access?.permission === 'exec' ? 'is-active' : '', "aria-pressed": access?.permission === 'exec', disabled: access === null, onClick: () => onPermission('exec'), children: "\u4EC5\u547D\u4EE4" }), _jsx("button", { type: "button", className: access?.permission === 'terminal' ? 'is-active' : '', "aria-pressed": access?.permission === 'terminal', disabled: access === null, onClick: () => onPermission('terminal'), children: "\u7EC8\u7AEF\u63A7\u5236" })] }), _jsxs("label", { className: "dsh-ssh-access-approval", children: [_jsxs("span", { children: [_jsx("strong", { children: "\u6267\u884C\u524D\u786E\u8BA4" }), _jsx("small", { children: access?.requireCommandApproval === false ? '关闭后按当前会话权限直接执行' : '需 DSH 使用 Ask;Full Access 会直接拒绝' })] }), _jsx("input", { type: "checkbox", checked: access?.requireCommandApproval ?? true, disabled: access === null, onChange: event => onApproval(event.target.checked) }), _jsx("i", { "aria-hidden": "true" })] }), error && _jsx("p", { className: "dsh-ssh-inline-error", role: "alert", children: error })] });
|
|
75
|
+
}
|
|
76
|
+
function RemoteProjectDialog({ profile, project, onClose, onSaved }) {
|
|
77
|
+
const [name, setName] = useState(project?.name ?? '');
|
|
78
|
+
const [path, setPath] = useState(project?.path ?? '');
|
|
79
|
+
const [saving, setSaving] = useState(false);
|
|
80
|
+
const [error, setError] = useState();
|
|
81
|
+
const submit = async (event) => {
|
|
82
|
+
event.preventDefault();
|
|
83
|
+
setSaving(true);
|
|
84
|
+
setError(undefined);
|
|
85
|
+
try {
|
|
86
|
+
if (project === undefined)
|
|
87
|
+
await createRemoteProject(profile.id, { name, path });
|
|
88
|
+
else
|
|
89
|
+
await updateRemoteProject(profile.id, project.id, { name, path });
|
|
90
|
+
await onSaved();
|
|
91
|
+
}
|
|
92
|
+
catch (reason) {
|
|
93
|
+
setError(message(reason));
|
|
94
|
+
setSaving(false);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const remove = async () => {
|
|
98
|
+
if (project === undefined || !window.confirm(`删除固定目录“${project.name}”?相关 DSH 会话不会删除。`))
|
|
99
|
+
return;
|
|
100
|
+
setSaving(true);
|
|
101
|
+
setError(undefined);
|
|
102
|
+
try {
|
|
103
|
+
await deleteRemoteProject(profile.id, project.id);
|
|
104
|
+
await onSaved();
|
|
105
|
+
}
|
|
106
|
+
catch (reason) {
|
|
107
|
+
setError(message(reason));
|
|
108
|
+
setSaving(false);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
return _jsx(Dialog, { className: "dsh-ssh-project-dialog", title: project === undefined ? '添加固定目录' : '编辑固定目录', subtitle: `${profile.name} · 终端与 SFTP 的默认远端路径`, onClose: onClose, children: _jsxs("form", { className: "dsh-ssh-form", onSubmit: event => { void submit(event); }, children: [_jsxs("label", { className: "dsh-ssh-field", children: [_jsx("span", { children: "\u540D\u79F0" }), _jsx("input", { required: true, maxLength: 80, value: name, placeholder: "\u7F51\u7AD9\u9879\u76EE", onChange: event => setName(event.target.value) })] }), _jsxs("label", { className: "dsh-ssh-field", children: [_jsx("span", { children: "\u8FDC\u7AEF\u8DEF\u5F84" }), _jsx("input", { required: true, maxLength: 4096, value: path, spellCheck: false, placeholder: "/var/www/example", onChange: event => setPath(event.target.value) }), _jsx("small", { children: "\u4FDD\u5B58\u524D\u4F1A\u5728\u53F3\u4FA7 SFTP \u4E2D\u9A8C\u8BC1\u8DEF\u5F84\u662F\u5426\u53EF\u8BBF\u95EE\u3002" })] }), error && _jsx("p", { className: "dsh-ssh-inline-error", role: "alert", children: error }), _jsxs("div", { className: "dsh-ssh-dialog-actions", children: [project !== undefined && _jsxs("button", { type: "button", className: "dsh-ssh-danger-button", disabled: saving, onClick: () => { void remove(); }, children: [_jsx(IconTrashOutline16, { size: 15 }), "\u5220\u9664"] }), _jsx("span", { className: "dsh-ssh-dialog-spacer" }), _jsx("button", { type: "button", className: "dsh-ssh-secondary-button", disabled: saving, onClick: onClose, children: "\u53D6\u6D88" }), _jsx("button", { className: "dsh-ssh-primary-button", disabled: saving, children: saving ? '保存中…' : '保存目录' })] })] }) });
|
|
112
|
+
}
|
|
113
|
+
function groupProfiles(profiles) {
|
|
114
|
+
const result = new Map();
|
|
115
|
+
for (const profile of profiles) {
|
|
116
|
+
const name = profile.group?.trim() || '未分组';
|
|
117
|
+
const current = result.get(name);
|
|
118
|
+
if (current === undefined)
|
|
119
|
+
result.set(name, [profile]);
|
|
120
|
+
else
|
|
121
|
+
current.push(profile);
|
|
122
|
+
}
|
|
123
|
+
return [...result].map(([name, grouped]) => ({ name, profiles: grouped }));
|
|
124
|
+
}
|
|
125
|
+
function searchText(profile) { return `${profile.name} ${profile.group ?? ''} ${profile.host} ${profile.username} ${profile.tags.join(' ')}`.toLocaleLowerCase(); }
|
|
126
|
+
function message(reason) { return reason instanceof Error ? reason.message : String(reason); }
|
|
127
|
+
//# sourceMappingURL=remote-workspace-tree.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-workspace-tree.js","sourceRoot":"","sources":["../src/remote-workspace-tree.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
interface ResizableSplitProps {
|
|
3
|
+
primary: ReactNode;
|
|
4
|
+
secondary: ReactNode;
|
|
5
|
+
storageKey: string;
|
|
6
|
+
label: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function ResizableSplit({ primary, secondary, storageKey, label }: ResizableSplitProps): JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=resizable-split.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resizable-split.d.ts","sourceRoot":"","sources":["../src/resizable-split.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EACf,MAAM,OAAO,CAAA;AASd,UAAU,mBAAmB;IAC3B,OAAO,EAAE,SAAS,CAAA;IAClB,SAAS,EAAE,SAAS,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;CACd;AAwCD,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,EAAE,mBAAmB,GAAG,GAAG,CAAC,OAAO,CA0F1G"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useRef, useState, } from 'react';
|
|
3
|
+
const DEFAULT_SECONDARY_WIDTH = 38;
|
|
4
|
+
const MIN_SECONDARY_WIDTH = 28;
|
|
5
|
+
const MAX_SECONDARY_WIDTH = 66;
|
|
6
|
+
const MIN_PRIMARY_PIXELS = 320;
|
|
7
|
+
const MIN_SECONDARY_PIXELS = 280;
|
|
8
|
+
const SEPARATOR_PIXELS = 7;
|
|
9
|
+
function clamp(value, minimum, maximum) {
|
|
10
|
+
return Math.min(maximum, Math.max(minimum, value));
|
|
11
|
+
}
|
|
12
|
+
function readStoredWidth(storageKey) {
|
|
13
|
+
if (typeof window === 'undefined')
|
|
14
|
+
return DEFAULT_SECONDARY_WIDTH;
|
|
15
|
+
try {
|
|
16
|
+
const stored = Number.parseFloat(window.localStorage.getItem(storageKey) ?? '');
|
|
17
|
+
return Number.isFinite(stored)
|
|
18
|
+
? clamp(stored, MIN_SECONDARY_WIDTH, MAX_SECONDARY_WIDTH)
|
|
19
|
+
: DEFAULT_SECONDARY_WIDTH;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return DEFAULT_SECONDARY_WIDTH;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function saveWidth(storageKey, width) {
|
|
26
|
+
try {
|
|
27
|
+
window.localStorage.setItem(storageKey, width.toFixed(2));
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Storage can be unavailable in privacy-restricted webviews. Resizing still works for this session.
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function boundsFor(containerWidth) {
|
|
34
|
+
if (containerWidth <= 0)
|
|
35
|
+
return { min: MIN_SECONDARY_WIDTH, max: MAX_SECONDARY_WIDTH };
|
|
36
|
+
const pixelMinimum = (MIN_SECONDARY_PIXELS / containerWidth) * 100;
|
|
37
|
+
const pixelMaximum = ((containerWidth - MIN_PRIMARY_PIXELS - SEPARATOR_PIXELS) / containerWidth) * 100;
|
|
38
|
+
const min = Math.max(MIN_SECONDARY_WIDTH, pixelMinimum);
|
|
39
|
+
const max = Math.max(min, Math.min(MAX_SECONDARY_WIDTH, pixelMaximum));
|
|
40
|
+
return { min, max };
|
|
41
|
+
}
|
|
42
|
+
export function ResizableSplit({ primary, secondary, storageKey, label }) {
|
|
43
|
+
const [secondaryWidth, setSecondaryWidth] = useState(() => readStoredWidth(storageKey));
|
|
44
|
+
const rootRef = useRef(null);
|
|
45
|
+
const separatorRef = useRef(null);
|
|
46
|
+
const dragWidthRef = useRef(secondaryWidth);
|
|
47
|
+
const pointerRef = useRef();
|
|
48
|
+
const applyWidth = (requestedWidth) => {
|
|
49
|
+
const root = rootRef.current;
|
|
50
|
+
if (root === null)
|
|
51
|
+
return requestedWidth;
|
|
52
|
+
const bounds = boundsFor(root.getBoundingClientRect().width);
|
|
53
|
+
const next = clamp(requestedWidth, bounds.min, bounds.max);
|
|
54
|
+
root.style.setProperty('--ssh-sftp-width', `${next}%`);
|
|
55
|
+
separatorRef.current?.setAttribute('aria-valuenow', String(Math.round(next)));
|
|
56
|
+
dragWidthRef.current = next;
|
|
57
|
+
return next;
|
|
58
|
+
};
|
|
59
|
+
const finishResize = (pointerId) => {
|
|
60
|
+
if (pointerRef.current === undefined)
|
|
61
|
+
return;
|
|
62
|
+
const separator = separatorRef.current;
|
|
63
|
+
pointerRef.current = undefined;
|
|
64
|
+
if (pointerId !== undefined && separator?.hasPointerCapture(pointerId))
|
|
65
|
+
separator.releasePointerCapture(pointerId);
|
|
66
|
+
rootRef.current?.classList.remove('is-resizing');
|
|
67
|
+
const next = dragWidthRef.current;
|
|
68
|
+
setSecondaryWidth(next);
|
|
69
|
+
saveWidth(storageKey, next);
|
|
70
|
+
};
|
|
71
|
+
const onPointerDown = (event) => {
|
|
72
|
+
if (event.button !== 0)
|
|
73
|
+
return;
|
|
74
|
+
pointerRef.current = event.pointerId;
|
|
75
|
+
dragWidthRef.current = secondaryWidth;
|
|
76
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
77
|
+
rootRef.current?.classList.add('is-resizing');
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
};
|
|
80
|
+
const onPointerMove = (event) => {
|
|
81
|
+
if (pointerRef.current !== event.pointerId)
|
|
82
|
+
return;
|
|
83
|
+
const rect = rootRef.current?.getBoundingClientRect();
|
|
84
|
+
if (rect === undefined || rect.width === 0)
|
|
85
|
+
return;
|
|
86
|
+
applyWidth(((rect.right - event.clientX) / rect.width) * 100);
|
|
87
|
+
event.preventDefault();
|
|
88
|
+
};
|
|
89
|
+
const onKeyDown = (event) => {
|
|
90
|
+
const step = event.shiftKey ? 5 : 2;
|
|
91
|
+
let requested;
|
|
92
|
+
if (event.key === 'ArrowLeft')
|
|
93
|
+
requested = secondaryWidth + step;
|
|
94
|
+
if (event.key === 'ArrowRight')
|
|
95
|
+
requested = secondaryWidth - step;
|
|
96
|
+
if (event.key === 'Home')
|
|
97
|
+
requested = MIN_SECONDARY_WIDTH;
|
|
98
|
+
if (event.key === 'End')
|
|
99
|
+
requested = MAX_SECONDARY_WIDTH;
|
|
100
|
+
if (requested === undefined)
|
|
101
|
+
return;
|
|
102
|
+
const next = applyWidth(requested);
|
|
103
|
+
setSecondaryWidth(next);
|
|
104
|
+
saveWidth(storageKey, next);
|
|
105
|
+
event.preventDefault();
|
|
106
|
+
};
|
|
107
|
+
const resetWidth = () => {
|
|
108
|
+
const next = applyWidth(DEFAULT_SECONDARY_WIDTH);
|
|
109
|
+
setSecondaryWidth(next);
|
|
110
|
+
saveWidth(storageKey, next);
|
|
111
|
+
};
|
|
112
|
+
const style = { '--ssh-sftp-width': `${secondaryWidth}%` };
|
|
113
|
+
return _jsxs("div", { ref: rootRef, className: "dsh-ssh-workbench-split", style: style, children: [primary, _jsx("div", { ref: separatorRef, className: "dsh-ssh-workbench-resizer", role: "separator", "aria-label": label, "aria-orientation": "vertical", "aria-valuemin": MIN_SECONDARY_WIDTH, "aria-valuemax": MAX_SECONDARY_WIDTH, "aria-valuenow": Math.round(secondaryWidth), tabIndex: 0, title: "\u62D6\u52A8\u8C03\u6574\u5BBD\u5EA6\uFF0C\u53CC\u51FB\u6062\u590D\u9ED8\u8BA4", onDoubleClick: resetWidth, onKeyDown: onKeyDown, onPointerDown: onPointerDown, onPointerMove: onPointerMove, onPointerUp: event => finishResize(event.pointerId), onPointerCancel: event => finishResize(event.pointerId), onLostPointerCapture: () => finishResize() }), secondary] });
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=resizable-split.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resizable-split.js","sourceRoot":"","sources":["../src/resizable-split.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InjectionView } from './client-api.js';
|
|
2
|
+
type SessionAccessListener = (value: InjectionView) => void;
|
|
3
|
+
export declare function publishSessionAccess(value: InjectionView): void;
|
|
4
|
+
export declare function subscribeSessionAccess(listener: SessionAccessListener): () => void;
|
|
5
|
+
export {};
|
|
6
|
+
//# sourceMappingURL=session-access-channel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-access-channel.d.ts","sourceRoot":"","sources":["../src/session-access-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,KAAK,qBAAqB,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;AAI3D,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CAE/D;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,qBAAqB,GAAG,MAAM,IAAI,CAGlF"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const listeners = new Set();
|
|
2
|
+
export function publishSessionAccess(value) {
|
|
3
|
+
for (const listener of listeners)
|
|
4
|
+
listener(value);
|
|
5
|
+
}
|
|
6
|
+
export function subscribeSessionAccess(listener) {
|
|
7
|
+
listeners.add(listener);
|
|
8
|
+
return () => listeners.delete(listener);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=session-access-channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-access-channel.js","sourceRoot":"","sources":["../src/session-access-channel.ts"],"names":[],"mappings":"AAIA,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAA;AAElD,MAAM,UAAU,oBAAoB,CAAC,KAAoB;IACvD,KAAK,MAAM,QAAQ,IAAI,SAAS;QAAE,QAAQ,CAAC,KAAK,CAAC,CAAA;AACnD,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,QAA+B;IACpE,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IACvB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;AACzC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type InjectionView } from './client-api.js';
|
|
2
|
+
export interface SessionAccessState {
|
|
3
|
+
value: InjectionView | null;
|
|
4
|
+
loading: boolean;
|
|
5
|
+
saving: boolean;
|
|
6
|
+
error?: string | undefined;
|
|
7
|
+
setProfiles(profileIds: string[]): void;
|
|
8
|
+
setDirectory(profileId: string, path?: string, projectId?: string): void;
|
|
9
|
+
setPermission(permission: InjectionView['permission']): void;
|
|
10
|
+
setRequireCommandApproval(value: boolean): void;
|
|
11
|
+
replace(value: InjectionView): Promise<InjectionView>;
|
|
12
|
+
}
|
|
13
|
+
export declare function useSessionAccess(sessionId?: string): SessionAccessState;
|
|
14
|
+
export declare function emptyAccess(sessionId: string): InjectionView;
|
|
15
|
+
//# sourceMappingURL=session-access.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-access.d.ts","sourceRoot":"","sources":["../src/session-access.ts"],"names":[],"mappings":"AACA,OAAO,EAAoC,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAGtF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,aAAa,GAAG,IAAI,CAAA;IAC3B,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC1B,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACvC,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxE,aAAa,CAAC,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC,GAAG,IAAI,CAAA;IAC5D,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IAC/C,OAAO,CAAC,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;CACtD;AAED,wBAAgB,gBAAgB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAgFvE;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,CAE5D"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { loadInjection, saveSessionAccess } from './client-api.js';
|
|
3
|
+
import { publishSessionAccess } from './session-access-channel.js';
|
|
4
|
+
export function useSessionAccess(sessionId) {
|
|
5
|
+
const [value, setValue] = useState(null);
|
|
6
|
+
const [loading, setLoading] = useState(sessionId !== undefined);
|
|
7
|
+
const [saving, setSaving] = useState(false);
|
|
8
|
+
const [error, setError] = useState();
|
|
9
|
+
const valueRef = useRef(null);
|
|
10
|
+
const sessionRef = useRef(sessionId);
|
|
11
|
+
const queueRef = useRef(Promise.resolve());
|
|
12
|
+
const pendingRef = useRef(0);
|
|
13
|
+
sessionRef.current = sessionId;
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
let cancelled = false;
|
|
16
|
+
valueRef.current = null;
|
|
17
|
+
setValue(null);
|
|
18
|
+
setError(undefined);
|
|
19
|
+
setLoading(sessionId !== undefined);
|
|
20
|
+
if (sessionId === undefined)
|
|
21
|
+
return () => { cancelled = true; };
|
|
22
|
+
void loadInjection(sessionId).then(stored => {
|
|
23
|
+
if (cancelled)
|
|
24
|
+
return;
|
|
25
|
+
const next = stored ?? emptyAccess(sessionId);
|
|
26
|
+
valueRef.current = next;
|
|
27
|
+
setValue(next);
|
|
28
|
+
publishSessionAccess(next);
|
|
29
|
+
}).catch(reason => { if (!cancelled)
|
|
30
|
+
setError(errorMessage(reason)); }).finally(() => { if (!cancelled)
|
|
31
|
+
setLoading(false); });
|
|
32
|
+
return () => { cancelled = true; };
|
|
33
|
+
}, [sessionId]);
|
|
34
|
+
const replace = useCallback(async (next) => {
|
|
35
|
+
valueRef.current = next;
|
|
36
|
+
setValue(next);
|
|
37
|
+
publishSessionAccess(next);
|
|
38
|
+
setError(undefined);
|
|
39
|
+
pendingRef.current += 1;
|
|
40
|
+
setSaving(true);
|
|
41
|
+
const request = queueRef.current.catch(() => undefined).then(() => saveSessionAccess(next));
|
|
42
|
+
queueRef.current = request;
|
|
43
|
+
try {
|
|
44
|
+
const stored = await request;
|
|
45
|
+
if (sessionRef.current === stored.sessionId && valueRef.current === next) {
|
|
46
|
+
valueRef.current = stored;
|
|
47
|
+
setValue(stored);
|
|
48
|
+
publishSessionAccess(stored);
|
|
49
|
+
}
|
|
50
|
+
return stored;
|
|
51
|
+
}
|
|
52
|
+
catch (reason) {
|
|
53
|
+
if (sessionRef.current === next.sessionId)
|
|
54
|
+
setError(errorMessage(reason));
|
|
55
|
+
throw reason;
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
pendingRef.current = Math.max(0, pendingRef.current - 1);
|
|
59
|
+
if (sessionRef.current === next.sessionId && pendingRef.current === 0)
|
|
60
|
+
setSaving(false);
|
|
61
|
+
}
|
|
62
|
+
}, []);
|
|
63
|
+
const update = useCallback((patch) => {
|
|
64
|
+
const currentSessionId = sessionRef.current;
|
|
65
|
+
if (currentSessionId === undefined)
|
|
66
|
+
return;
|
|
67
|
+
const current = valueRef.current ?? emptyAccess(currentSessionId);
|
|
68
|
+
void replace({ ...current, ...patch, sessionId: currentSessionId }).catch(() => { });
|
|
69
|
+
}, [replace]);
|
|
70
|
+
return {
|
|
71
|
+
value, loading, saving, error,
|
|
72
|
+
setProfiles: profileIds => update({ profileIds }),
|
|
73
|
+
setDirectory: (profileId, path, projectId) => {
|
|
74
|
+
const currentSessionId = sessionRef.current;
|
|
75
|
+
if (currentSessionId === undefined)
|
|
76
|
+
return;
|
|
77
|
+
const current = valueRef.current ?? emptyAccess(currentSessionId);
|
|
78
|
+
const workingDirectories = { ...current.workingDirectories };
|
|
79
|
+
const workingProjectIds = { ...current.workingProjectIds };
|
|
80
|
+
if (path === undefined)
|
|
81
|
+
delete workingDirectories[profileId];
|
|
82
|
+
else
|
|
83
|
+
workingDirectories[profileId] = path;
|
|
84
|
+
if (projectId === undefined)
|
|
85
|
+
delete workingProjectIds[profileId];
|
|
86
|
+
else
|
|
87
|
+
workingProjectIds[profileId] = projectId;
|
|
88
|
+
update({ workingDirectories, workingProjectIds });
|
|
89
|
+
},
|
|
90
|
+
setPermission: permission => update({ permission }),
|
|
91
|
+
setRequireCommandApproval: requireCommandApproval => update({ requireCommandApproval }),
|
|
92
|
+
replace,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
export function emptyAccess(sessionId) {
|
|
96
|
+
return { sessionId, profileIds: [], permission: 'exec', requireCommandApproval: true, workingDirectories: {}, workingProjectIds: {}, updatedAt: 0 };
|
|
97
|
+
}
|
|
98
|
+
function errorMessage(reason) { return reason instanceof Error ? reason.message : String(reason); }
|
|
99
|
+
//# sourceMappingURL=session-access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-access.js","sourceRoot":"","sources":["../src/session-access.ts"],"names":[],"mappings":"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"}
|
package/lib/sftp-client.d.ts
CHANGED
|
@@ -9,7 +9,11 @@ export declare function ActivitySftpBrowser({ sessionId, profile, profiles, onPr
|
|
|
9
9
|
onProfile(id: string): void;
|
|
10
10
|
onSaved(): Promise<void>;
|
|
11
11
|
}): JSX.Element;
|
|
12
|
-
export declare function ProfileSftpPane({ profile }: {
|
|
12
|
+
export declare function ProfileSftpPane({ profile, initialPath, onEdit, onDelete, embedded }: {
|
|
13
13
|
profile: ProfileView;
|
|
14
|
+
initialPath?: string;
|
|
15
|
+
onEdit?(): void;
|
|
16
|
+
onDelete?(): void;
|
|
17
|
+
embedded?: boolean;
|
|
14
18
|
}): JSX.Element;
|
|
15
19
|
//# sourceMappingURL=sftp-client.d.ts.map
|
package/lib/sftp-client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sftp-client.d.ts","sourceRoot":"","sources":["../src/sftp-client.tsx"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAC3C,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"sftp-client.d.ts","sourceRoot":"","sources":["../src/sftp-client.tsx"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,mBAAmB,EAAE,KAAK,WAAW,EAC3C,MAAM,iBAAiB,CAAA;AAoBxB,wBAAgB,qBAAqB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,GAAG,CAAC,OAAO,CAKvF;AAED,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,mBAAmB,CAAC;IAAC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,GAAG,GAAG,CAAC,OAAO,CAclP;AAED,wBAAgB,eAAe,CAAC,EAAE,OAAO,EAAE,WAAiB,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAgB,EAAE,EAAE;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IAAC,QAAQ,CAAC,IAAI,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,GAAG,CAAC,OAAO,CASvN"}
|