@lemoncat7/dsh-ssh 1.7.0 → 1.9.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.
Files changed (128) hide show
  1. package/ARCHITECTURE.md +15 -0
  2. package/README.md +6 -2
  3. package/docs/dialog-consistency.md +26 -0
  4. package/docs/releases/1.8.0.md +13 -0
  5. package/docs/releases/1.9.0.md +15 -0
  6. package/lib/api.d.ts.map +1 -1
  7. package/lib/api.js +58 -1
  8. package/lib/api.js.map +1 -1
  9. package/lib/browser-transfers.d.ts +26 -0
  10. package/lib/browser-transfers.d.ts.map +1 -0
  11. package/lib/browser-transfers.js +72 -0
  12. package/lib/browser-transfers.js.map +1 -0
  13. package/lib/client-api.d.ts +8 -0
  14. package/lib/client-api.d.ts.map +1 -1
  15. package/lib/client-api.js +45 -8
  16. package/lib/client-api.js.map +1 -1
  17. package/lib/client-download.d.ts +22 -0
  18. package/lib/client-download.d.ts.map +1 -0
  19. package/lib/client-download.js +65 -0
  20. package/lib/client-download.js.map +1 -0
  21. package/lib/client.d.ts.map +1 -1
  22. package/lib/client.js +262 -151
  23. package/lib/client.js.map +4 -4
  24. package/lib/commands-panel.d.ts.map +1 -1
  25. package/lib/commands-panel.js +18 -5
  26. package/lib/commands-panel.js.map +1 -1
  27. package/lib/confirmation-dialog.d.ts +12 -0
  28. package/lib/confirmation-dialog.d.ts.map +1 -0
  29. package/lib/confirmation-dialog.js +41 -0
  30. package/lib/confirmation-dialog.js.map +1 -0
  31. package/lib/connector.d.ts.map +1 -1
  32. package/lib/connector.js +6 -4
  33. package/lib/connector.js.map +1 -1
  34. package/lib/domain.d.ts +10 -0
  35. package/lib/domain.d.ts.map +1 -1
  36. package/lib/domain.js.map +1 -1
  37. package/lib/download-progress.d.ts +17 -0
  38. package/lib/download-progress.d.ts.map +1 -0
  39. package/lib/download-progress.js +68 -0
  40. package/lib/download-progress.js.map +1 -0
  41. package/lib/endpoint-upload.d.ts +10 -0
  42. package/lib/endpoint-upload.d.ts.map +1 -0
  43. package/lib/endpoint-upload.js +63 -0
  44. package/lib/endpoint-upload.js.map +1 -0
  45. package/lib/file-entry-delete-dialog.js +1 -1
  46. package/lib/file-entry-delete-dialog.js.map +1 -1
  47. package/lib/file-entry-sort.d.ts +1 -0
  48. package/lib/file-entry-sort.d.ts.map +1 -1
  49. package/lib/file-entry-sort.js +5 -8
  50. package/lib/file-entry-sort.js.map +1 -1
  51. package/lib/file-transfer-tools.d.ts.map +1 -1
  52. package/lib/file-transfer-tools.js +40 -7
  53. package/lib/file-transfer-tools.js.map +1 -1
  54. package/lib/file-transfer-workspace.d.ts.map +1 -1
  55. package/lib/file-transfer-workspace.js +70 -26
  56. package/lib/file-transfer-workspace.js.map +1 -1
  57. package/lib/ftp-adapter.d.ts.map +1 -1
  58. package/lib/ftp-adapter.js +12 -3
  59. package/lib/ftp-adapter.js.map +1 -1
  60. package/lib/ftp-list-date.d.ts +3 -0
  61. package/lib/ftp-list-date.d.ts.map +1 -0
  62. package/lib/ftp-list-date.js +20 -0
  63. package/lib/ftp-list-date.js.map +1 -0
  64. package/lib/ftp-listing-client.d.ts +7 -0
  65. package/lib/ftp-listing-client.d.ts.map +1 -0
  66. package/lib/ftp-listing-client.js +28 -0
  67. package/lib/ftp-listing-client.js.map +1 -0
  68. package/lib/ftp-profile-editor.js +8 -6
  69. package/lib/ftp-profile-editor.js.map +1 -1
  70. package/lib/gist-sync.d.ts +4 -2
  71. package/lib/gist-sync.d.ts.map +1 -1
  72. package/lib/gist-sync.js +49 -12
  73. package/lib/gist-sync.js.map +1 -1
  74. package/lib/group-proxy-editor.d.ts +10 -0
  75. package/lib/group-proxy-editor.d.ts.map +1 -0
  76. package/lib/group-proxy-editor.js +33 -0
  77. package/lib/group-proxy-editor.js.map +1 -0
  78. package/lib/group-proxy.d.ts +7 -0
  79. package/lib/group-proxy.d.ts.map +1 -0
  80. package/lib/group-proxy.js +29 -0
  81. package/lib/group-proxy.js.map +1 -0
  82. package/lib/locales/messages.d.ts +98 -2
  83. package/lib/locales/messages.d.ts.map +1 -1
  84. package/lib/locales/messages.js +26 -2
  85. package/lib/locales/messages.js.map +1 -1
  86. package/lib/profile-editor.js +3 -2
  87. package/lib/profile-editor.js.map +1 -1
  88. package/lib/remote-files.d.ts +1 -0
  89. package/lib/remote-files.d.ts.map +1 -1
  90. package/lib/remote-files.js.map +1 -1
  91. package/lib/remote-workspace-tree.d.ts +1 -0
  92. package/lib/remote-workspace-tree.d.ts.map +1 -1
  93. package/lib/remote-workspace-tree.js +6 -13
  94. package/lib/remote-workspace-tree.js.map +1 -1
  95. package/lib/session-access.d.ts +1 -0
  96. package/lib/session-access.d.ts.map +1 -1
  97. package/lib/session-access.js +1 -0
  98. package/lib/session-access.js.map +1 -1
  99. package/lib/session-file-upload.d.ts +22 -0
  100. package/lib/session-file-upload.d.ts.map +1 -0
  101. package/lib/session-file-upload.js +56 -0
  102. package/lib/session-file-upload.js.map +1 -0
  103. package/lib/sftp-client.d.ts.map +1 -1
  104. package/lib/sftp-client.js +7 -5
  105. package/lib/sftp-client.js.map +1 -1
  106. package/lib/store.d.ts +4 -0
  107. package/lib/store.d.ts.map +1 -1
  108. package/lib/store.js +15 -0
  109. package/lib/store.js.map +1 -1
  110. package/lib/terminal-workspace.js +1 -1
  111. package/lib/terminal-workspace.js.map +1 -1
  112. package/lib/transfer-task-list.d.ts +16 -0
  113. package/lib/transfer-task-list.d.ts.map +1 -0
  114. package/lib/transfer-task-list.js +35 -0
  115. package/lib/transfer-task-list.js.map +1 -0
  116. package/lib/ui-components.d.ts +3 -1
  117. package/lib/ui-components.d.ts.map +1 -1
  118. package/lib/ui-components.js +6 -3
  119. package/lib/ui-components.js.map +1 -1
  120. package/lib/use-workspace-refresh.d.ts +3 -0
  121. package/lib/use-workspace-refresh.d.ts.map +1 -0
  122. package/lib/use-workspace-refresh.js +44 -0
  123. package/lib/use-workspace-refresh.js.map +1 -0
  124. package/lib/workspace-refresh.d.ts +14 -0
  125. package/lib/workspace-refresh.d.ts.map +1 -0
  126. package/lib/workspace-refresh.js +36 -0
  127. package/lib/workspace-refresh.js.map +1 -0
  128. package/package.json +1 -1
@@ -7,6 +7,7 @@ export interface SortableFileEntry {
7
7
  navigable?: boolean;
8
8
  size: number;
9
9
  modifiedAt: number;
10
+ modifiedAtText?: string;
10
11
  }
11
12
  /** Returns a sorted copy. Navigable directories stay above files in every mode. */
12
13
  export declare function sortFileEntries<T extends SortableFileEntry>(entries: readonly T[], key: FileEntrySortKey, direction: FileEntrySortDirection): T[];
@@ -1 +1 @@
1
- {"version":3,"file":"file-entry-sort.d.ts","sourceRoot":"","sources":["../src/file-entry-sort.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,CAAA;AAC7D,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,MAAM,CAAA;AAEnD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAA;IAChD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,CAAC,SAAS,iBAAiB,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,gBAAgB,EAAE,SAAS,EAAE,sBAAsB,GAAG,CAAC,EAAE,CAajJ"}
1
+ {"version":3,"file":"file-entry-sort.d.ts","sourceRoot":"","sources":["../src/file-entry-sort.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,MAAM,GAAG,YAAY,CAAA;AAC7D,MAAM,MAAM,sBAAsB,GAAG,KAAK,GAAG,MAAM,CAAA;AAEnD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAA;IAChD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,mFAAmF;AACnF,wBAAgB,eAAe,CAAC,CAAC,SAAS,iBAAiB,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,EAAE,GAAG,EAAE,gBAAgB,EAAE,SAAS,EAAE,sBAAsB,GAAG,CAAC,EAAE,CAcjJ"}
@@ -1,24 +1,21 @@
1
1
  import { compareFileNames } from './file-name-compare.js';
2
+ import { ftpListDateSortValue } from './ftp-list-date.js';
2
3
  /** Returns a sorted copy. Navigable directories stay above files in every mode. */
3
4
  export function sortFileEntries(entries, key, direction) {
4
5
  const multiplier = direction === 'asc' ? 1 : -1;
5
- return entries.map((entry, index) => ({ entry, index })).sort((left, right) => {
6
+ const now = Date.now();
7
+ return entries.map((entry, index) => ({ entry, index, value: key === 'modifiedAt' ? entry.modifiedAt || ftpListDateSortValue(entry.modifiedAtText, now) : entry.size })).sort((left, right) => {
6
8
  const leftDirectory = left.entry.kind === 'directory' || left.entry.navigable === true;
7
9
  const rightDirectory = right.entry.kind === 'directory' || right.entry.navigable === true;
8
10
  if (leftDirectory !== rightDirectory)
9
11
  return leftDirectory ? -1 : 1;
10
- const compared = compareValue(left.entry, right.entry, key);
12
+ const compared = key === 'name' ? compareFileNames(left.entry.name, right.entry.name) : left.value - right.value;
11
13
  if (compared !== 0)
12
14
  return compared * multiplier;
13
15
  const byName = key === 'name' ? 0 : compareFileNames(left.entry.name, right.entry.name);
14
16
  if (byName !== 0)
15
- return byName;
17
+ return byName * multiplier;
16
18
  return left.index - right.index;
17
19
  }).map(item => item.entry);
18
20
  }
19
- function compareValue(left, right, key) {
20
- if (key === 'name')
21
- return compareFileNames(left.name, right.name);
22
- return left[key] - right[key];
23
- }
24
21
  //# sourceMappingURL=file-entry-sort.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-entry-sort.js","sourceRoot":"","sources":["../src/file-entry-sort.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAczD,mFAAmF;AACnF,MAAM,UAAU,eAAe,CAA8B,OAAqB,EAAE,GAAqB,EAAE,SAAiC;IAC1I,MAAM,UAAU,GAAG,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC/C,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC5E,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,IAAI,CAAA;QACtF,MAAM,cAAc,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,KAAK,IAAI,CAAA;QACzF,IAAI,aAAa,KAAK,cAAc;YAAE,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAEnE,MAAM,QAAQ,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;QAC3D,IAAI,QAAQ,KAAK,CAAC;YAAE,OAAO,QAAQ,GAAG,UAAU,CAAA;QAChD,MAAM,MAAM,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACvF,IAAI,MAAM,KAAK,CAAC;YAAE,OAAO,MAAM,CAAA;QAC/B,OAAO,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;IACjC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AAC5B,CAAC;AAED,SAAS,YAAY,CAAC,IAAuB,EAAE,KAAwB,EAAE,GAAqB;IAC5F,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAA;IAClE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAA;AAC/B,CAAC"}
1
+ {"version":3,"file":"file-entry-sort.js","sourceRoot":"","sources":["../src/file-entry-sort.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAezD,mFAAmF;AACnF,MAAM,UAAU,eAAe,CAA8B,OAAqB,EAAE,GAAqB,EAAE,SAAiC;IAC1I,MAAM,UAAU,GAAG,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IACtB,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,KAAK,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,IAAI,oBAAoB,CAAC,KAAK,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QAC5L,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,KAAK,IAAI,CAAA;QACtF,MAAM,cAAc,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,KAAK,CAAC,SAAS,KAAK,IAAI,CAAA;QACzF,IAAI,aAAa,KAAK,cAAc;YAAE,OAAO,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAEnE,MAAM,QAAQ,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;QAChH,IAAI,QAAQ,KAAK,CAAC;YAAE,OAAO,QAAQ,GAAG,UAAU,CAAA;QAChD,MAAM,MAAM,GAAG,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QACvF,IAAI,MAAM,KAAK,CAAC;YAAE,OAAO,MAAM,GAAG,UAAU,CAAA;QAC5C,OAAO,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;IACjC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;AAC5B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-transfer-tools.d.ts","sourceRoot":"","sources":["../src/file-transfer-tools.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAOrC,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,mBAAmB,GAAG,MAAM,IAAI,CAY7I"}
1
+ {"version":3,"file":"file-transfer-tools.d.ts","sourceRoot":"","sources":["../src/file-transfer-tools.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAQrC,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,mBAAmB,GAAG,MAAM,IAAI,CAa7I"}
@@ -1,24 +1,53 @@
1
1
  import { defineTool } from '@deepseek-ai/dsh-tools';
2
2
  import { SessionFileDownloads } from './session-file-download.js';
3
- const FILE_TOOL_NAMES = new Set(['file_endpoint_list', 'file_directory_list', 'file_transfer_start', 'file_transfer_status', 'file_transfer_cancel', 'file_download_to_local']);
4
- const TRANSFER_TOOL_NAMES = new Set(['file_transfer_start', 'file_transfer_cancel', 'file_download_to_local']);
3
+ import { SessionFileUploads } from './session-file-upload.js';
4
+ const FILE_TOOL_NAMES = new Set(['file_endpoint_list', 'file_directory_list', 'file_transfer_start', 'file_transfer_status', 'file_transfer_cancel', 'file_download_to_local', 'file_upload_from_local']);
5
+ const TRANSFER_TOOL_NAMES = new Set(['file_transfer_start', 'file_transfer_cancel', 'file_download_to_local', 'file_upload_from_local']);
5
6
  const output = { schema: { type: 'string' }, render: (_args, value) => [{ type: 'text', text: String(value) }] };
6
7
  export function registerFileTransferTools(ctx, store, files, transfers) {
7
8
  const downloads = new SessionFileDownloads(files);
8
- const definitions = [endpointListTool(store, files), directoryListTool(store, files), transferStartTool(store, transfers), transferStatusTool(store, transfers), transferCancelTool(store, transfers), downloadTool(store, downloads)];
9
+ const uploads = new SessionFileUploads(files);
10
+ const definitions = [endpointListTool(store, files), directoryListTool(store, files), transferStartTool(store, transfers), transferStatusTool(store, transfers), transferCancelTool(store, transfers), downloadTool(store, downloads), uploadTool(store, uploads)];
9
11
  const disposers = definitions.map(definition => ctx.tools.register(definition));
10
12
  const disposeApproval = ctx.on('tools/pre-execute', async (exec, next) => {
11
- if (exec.name !== 'file_transfer_start' && exec.name !== 'file_download_to_local')
13
+ if (exec.name !== 'file_transfer_start' && exec.name !== 'file_download_to_local' && exec.name !== 'file_upload_from_local')
12
14
  return next();
13
15
  const injection = exec.agent === undefined ? undefined : store.injection(exec.agent.session.id);
14
16
  if (injection?.requireFileApproval !== true)
15
17
  return next();
16
- return { kind: 'ask', reason: exec.name === 'file_download_to_local' ? 'Download an authorized remote file into the current DSH session directory.' : 'The conversation requested a remote file transfer between authorized endpoints.' };
18
+ return { kind: 'ask', reason: exec.name === 'file_download_to_local' ? 'Download an authorized remote file into the current DSH session directory.' : exec.name === 'file_upload_from_local' ? 'Upload a file from the current DSH session directory to an authorized remote endpoint.' : 'The conversation requested a remote file transfer between authorized endpoints.' };
17
19
  });
18
20
  const disposeVisibility = installVisibility(ctx, store);
19
- return () => { downloads.close(); disposeVisibility(); disposeApproval(); for (const dispose of disposers.reverse())
21
+ return () => { uploads.close(); downloads.close(); disposeVisibility(); disposeApproval(); for (const dispose of disposers.reverse())
20
22
  dispose(); };
21
23
  }
24
+ function requireLocalAccess(store, exec, direction) {
25
+ const injection = requireFileInjection(store, exec);
26
+ if ((direction === 'upload' ? injection.allowLocalUpload : injection.allowLocalDownload) !== true)
27
+ throw new Error(`请在文件传输 → 会话授权中开启${direction === 'upload' ? '本地上传' : '本地下载'}权限`);
28
+ }
29
+ function uploadTool(store, uploads) {
30
+ return tool('file_upload_from_local', 'Upload one regular file from the current DSH session directory to an authorized FTP/FTPS/SFTP directory. localPath must be relative to the session directory, not the browser computer. Requires transfer AND explicit local upload permission; approval policy applies. Maximum 512 MiB, 5 minutes, no overwrite. Returns remotePath only after saving. Prefer this over shell/encoding/temporary servers.', {
31
+ endpointId: { type: 'string', required: true },
32
+ localPath: { type: 'string', required: true, description: 'Relative file path within the DSH session directory.' },
33
+ remoteDirectory: { type: 'string', required: true },
34
+ remoteName: { type: 'string', description: 'Optional filename, no directory separators.' },
35
+ }, async (raw, exec) => {
36
+ const args = object(raw), endpointId = text(args.endpointId, 'endpointId', 110);
37
+ const owner = requireEndpoint(store, exec, endpointId, true);
38
+ requireLocalAccess(store, exec, 'upload');
39
+ const sessionDirectory = owner.session.header.cwd;
40
+ if (!sessionDirectory)
41
+ throw new Error('当前会话没有本地工作目录');
42
+ const assertAllowed = () => {
43
+ requireEndpoint(store, exec, endpointId, true);
44
+ requireLocalAccess(store, exec, 'upload');
45
+ if (owner.session.header.cwd !== sessionDirectory)
46
+ throw new Error('会话目录已更新,请重试');
47
+ };
48
+ return json(await uploads.upload({ endpointId, sessionDirectory, localPath: rawText(args.localPath, 'localPath', 4096), remoteDirectory: rawText(args.remoteDirectory, 'remoteDirectory', 4096), ...(args.remoteName === undefined ? {} : { remoteName: rawText(args.remoteName, 'remoteName', 255) }) }, exec.signal, assertAllowed));
49
+ });
50
+ }
22
51
  function downloadTool(store, downloads) {
23
52
  return tool('file_download_to_local', 'Download one file from an authorized FTP, FTPS or SFTP endpoint into the current DSH session directory. Default: preserve the remote filename directly in that directory. Optional localPath is relative to the session directory; its parent must already exist. Never overwrite existing files. Returns the actual saved localPath only after completion. This is DSH-host local storage, NOT the browser Downloads folder. Prefer this tool over SSH commands, temporary servers or terminal encoding. Requires file transfer permission/approval. Maximum 512 MiB per file, 5 minutes; cancellation/failure removes staging data. Directory download is not supported.', {
24
53
  endpointId: { type: 'string', required: true, description: 'Exact authorized endpoint id from file_endpoint_list.' },
@@ -27,11 +56,13 @@ function downloadTool(store, downloads) {
27
56
  }, async (raw, exec) => {
28
57
  const args = object(raw), endpointId = text(args.endpointId, 'endpointId', 110);
29
58
  const owner = requireEndpoint(store, exec, endpointId, true);
59
+ requireLocalAccess(store, exec, 'download');
30
60
  const sessionDirectory = owner.session.header.cwd;
31
61
  if (!sessionDirectory)
32
62
  throw new Error('当前会话没有本地工作目录,不能下载');
33
63
  const assertAllowed = () => {
34
64
  requireEndpoint(store, exec, endpointId, true);
65
+ requireLocalAccess(store, exec, 'download');
35
66
  if (owner.session.header.cwd !== sessionDirectory)
36
67
  throw new Error('会话目录已更新,请重新下载');
37
68
  };
@@ -42,7 +73,7 @@ function downloadTool(store, downloads) {
42
73
  function endpointListTool(store, files) {
43
74
  return tool('file_endpoint_list', 'List remote file endpoints explicitly authorized for this DSH conversation. Credentials and unauthorized endpoints are never returned.', {}, async (_raw, exec) => {
44
75
  const injection = requireFileInjection(store, exec);
45
- return json({ permission: injection.filePermission, endpoints: injection.fileEndpointIds.map(id => files.endpoint(id)).filter(Boolean) });
76
+ return json({ permission: injection.filePermission, allowLocalUpload: injection.allowLocalUpload === true, allowLocalDownload: injection.allowLocalDownload === true, endpoints: injection.fileEndpointIds.map(id => files.endpoint(id)).filter(Boolean) });
46
77
  }, true);
47
78
  }
48
79
  function directoryListTool(store, files) {
@@ -152,11 +183,13 @@ function applyVisibility(assembly, injection) {
152
183
  }
153
184
  if (injection.filePermission === 'browse')
154
185
  assembly.tools = assembly.tools.filter(schema => !TRANSFER_TOOL_NAMES.has(schema.name));
186
+ assembly.tools = assembly.tools.filter(schema => (schema.name !== 'file_upload_from_local' || injection.allowLocalUpload === true) && (schema.name !== 'file_download_to_local' || injection.allowLocalDownload === true));
155
187
  assembly.contexts.push({
156
188
  name: 'dsh-ssh:file-access',
157
189
  text: `Remote file access is limited to explicitly authorized endpoints. Permission: ${injection.filePermission}.
158
190
  For any file listing or transfer request, use file_endpoint_list, file_directory_list, and the available file_transfer_* tools before considering SSH commands. Never guess endpoint ids or paths.
159
191
  For downloading a remote file for this conversation, use file_download_to_local. It defaults to the current DSH session directory; report its returned localPath. Do not invent a sandbox URL or claim it was saved to the browser's Downloads folder.
192
+ Use file_upload_from_local to upload a session-directory file when explicitly authorized. Local upload/download have separate opt-in permissions; do not bypass missing permission with SSH. Browser computer uploads must be selected by the user in 文件传输.
160
193
  Never start an HTTP or other file server, open a temporary port, encode a file through a terminal, or invent another transport for file delivery.
161
194
  For a download to the user's browser-local computer, do not run terminal commands: directly tell the user to select the file in SSH → 文件传输 and click “下载到本地”. The conversation tool cannot attach those remote bytes itself.
162
195
  If the requested source or destination is not authorized, explain that directly instead of attempting a workaround.`,
@@ -1 +1 @@
1
- {"version":3,"file":"file-transfer-tools.js","sourceRoot":"","sources":["../src/file-transfer-tools.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file-transfer-tools.js","sourceRoot":"","sources":["../src/file-transfer-tools.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"file-transfer-workspace.d.ts","sourceRoot":"","sources":["../src/file-transfer-workspace.tsx"],"names":[],"mappings":"AAIA,OAAO,EAEkB,KAAK,cAAc,EAAE,KAAK,cAAc,EAAoE,KAAK,cAAc,EACvJ,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAmB7D,wBAAgB,qBAAqB,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,iBAAiB,EAAE,EAAE;IAAE,WAAW,EAAE,cAAc,EAAE,CAAC;IAAC,YAAY,EAAE,cAAc,EAAE,CAAC;IAAC,YAAY,EAAE,cAAc,EAAE,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAC;IAAC,iBAAiB,IAAI,IAAI,CAAA;CAAE,GAAG,GAAG,CAAC,OAAO,CAuGnR"}
1
+ {"version":3,"file":"file-transfer-workspace.d.ts","sourceRoot":"","sources":["../src/file-transfer-workspace.tsx"],"names":[],"mappings":"AAMA,OAAO,EAGkB,KAAK,cAAc,EAAE,KAAK,cAAc,EAAoE,KAAK,cAAc,EACvJ,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAmB7D,wBAAgB,qBAAqB,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,iBAAiB,EAAE,EAAE;IAAE,WAAW,EAAE,cAAc,EAAE,CAAC;IAAC,YAAY,EAAE,cAAc,EAAE,CAAC;IAAC,YAAY,EAAE,cAAc,EAAE,CAAC;IAAC,MAAM,EAAE,kBAAkB,CAAC;IAAC,iBAAiB,IAAI,IAAI,CAAA;CAAE,GAAG,GAAG,CAAC,OAAO,CAyGnR"}
@@ -1,9 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useSshLocale } from './use-ssh-locale.js';
3
+ import { trackDownloadClick } from './browser-transfers.js';
4
+ import { BrowserTransferTask, TransferTaskList, isBrowserTransferActive, useBrowserTransferTasks } from './transfer-task-list.js';
3
5
  import { t } from './i18n.js';
4
6
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
5
- import { IconChevronDownOutline14, IconChevronLeftOutline14, IconCloseOutline16, IconDataOutline16, IconPlusOutline16, IconTrashOutline16 } from '@deepseek-ai/dsh-client-ui-primitives';
6
- import { inspectFileEndpointEntry, cancelFileTransfer, deleteFileEndpointEntries, fileEndpointDownloadUrl, loadFileEndpointDirectory, loadFileEndpoints, loadTransferJobs, startFileTransfer, } from './client-api.js';
7
+ import { IconChevronLeftOutline14, IconCloseOutline16, IconDataOutline16, IconPlusOutline16, IconTrashOutline16 } from '@deepseek-ai/dsh-client-ui-primitives';
8
+ import { uploadFileEndpointFile, inspectFileEndpointEntry, cancelFileTransfer, deleteFileEndpointEntries, fileEndpointDownloadUrl, loadFileEndpointDirectory, loadFileEndpoints, loadTransferJobs, startFileTransfer, } from './client-api.js';
7
9
  import { Dialog, errorMessage } from './ui-components.js';
8
10
  import { FtpConnectionsDialog } from './ftp-profile-editor.js';
9
11
  import { REMOTE_FILES_DRAG_TYPE, canTransferIntoRemoteDirectory, isNavigableRemoteEntry, isSameRemoteTransferLocation, parseRemoteFilesDragPayload, } from './file-transfer-intent.js';
@@ -11,7 +13,7 @@ import { executeRemoteFileDrop } from './remote-file-drop.js';
11
13
  import { FileEntryDeleteDialog } from './file-entry-delete-dialog.js';
12
14
  import { sortFileEntries } from './file-entry-sort.js';
13
15
  import { useBorderGlowSurface } from './border-glow.js';
14
- const STORAGE_KEY = 'dsh-ssh:file-transfer:tabs:v2';
16
+ const STORAGE_KEY = 'dsh-ssh:file-transfer:tabs:v3';
15
17
  const FILE_ROW_HEIGHT = 38;
16
18
  const transferClockFormatter = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' });
17
19
  export function FileTransferWorkspace({ ftpProfiles, vaultEntries, proxyEntries, access, onProfilesChanged }) {
@@ -130,17 +132,17 @@ export function FileTransferWorkspace({ ftpProfiles, vaultEntries, proxyEntries,
130
132
  setError(errorMessage(reason));
131
133
  }
132
134
  };
133
- return _jsxs("div", { className: "dsh-ssh-transfer-workspace", children: [_jsxs("header", { className: "dsh-ssh-transfer-header", children: [_jsxs("div", { children: [_jsx("h1", { children: t("client.fileTransfer") }), _jsx("p", { children: t("file-transfer-workspace.directStreamingTransfersBetweenFtpFtpsAndSftp") })] }), _jsxs("div", { className: "dsh-ssh-transfer-actions", children: [_jsxs("button", { type: "button", "data-ssh-interactive": "control", className: "dsh-ssh-secondary-button", onClick: () => setAccessOpen(true), children: [t("file-transfer-workspace.sessionAccess"), " ", access.value?.fileEndpointIds.length ?? 0] }), _jsx("button", { type: "button", "data-ssh-interactive": "control", className: "dsh-ssh-secondary-button", onClick: () => setConnectionsOpen(true), children: t("file-transfer-workspace.ftpManager") })] })] }), error && _jsxs("div", { className: "dsh-ssh-banner is-error", role: "alert", children: [_jsx("span", { children: error }), _jsx("button", { type: "button", onClick: () => setError(undefined), "aria-label": t("client.close"), children: _jsx(IconCloseOutline16, { size: 16 }) })] }), _jsxs("section", { ref: stageGlow.ref, onPointerMove: stageGlow.onPointerMove, onPointerLeave: stageGlow.onPointerLeave, className: "dsh-ssh-transfer-stage dsh-ssh-border-surface", children: [_jsxs("div", { className: "dsh-ssh-transfer-tabbar", role: "tablist", "aria-label": t("file-transfer-workspace.fileTransferTasksPage"), children: [_jsxs("div", { className: "dsh-ssh-transfer-tabs", children: [tabs.map(tab => _jsxs("span", { "data-ssh-interactive": "choice", className: `dsh-ssh-transfer-tab${tab.id === active?.id ? ' is-active' : ''}`, children: [_jsx("button", { type: "button", role: "tab", "aria-selected": tab.id === active?.id, onClick: () => setActiveTabId(tab.id), children: transferTabLabel(tab) }), tabs.length > 1 && _jsx("button", { type: "button", className: "is-close", "aria-label": t("file-transfer-workspace.close", [transferTabLabel(tab)]), onClick: () => closeTab(tab.id), children: _jsx(IconCloseOutline16, { size: 13 }) })] }, tab.id)), _jsx("button", { type: "button", "data-ssh-interactive": "control", className: "is-add", "aria-label": t("file-transfer-workspace.newTransferTasksPage"), onClick: addTab, children: _jsx(IconPlusOutline16, { size: 15 }) })] }), _jsx("div", { className: "dsh-ssh-pane-layout", "aria-label": t("file-transfer-workspace.panes"), children: [2, 3, 4].map(count => _jsxs("button", { type: "button", "data-ssh-interactive": "choice", "aria-pressed": active?.panes.length === count, className: active?.panes.length === count ? 'is-active' : '', onClick: () => setPaneCount(count), children: [count, " ", t("file-transfer-workspace.panes2")] }, count)) })] }), endpoints.length === 0 ? _jsxs("div", { className: "dsh-ssh-transfer-empty", children: [_jsx(IconDataOutline16, { size: 24 }), _jsx("strong", { children: t("file-transfer-workspace.noFileConnectionsAvailableYet") }), _jsx("p", { children: t("file-transfer-workspace.createAnFtpFtpsConnectionOrAddAnSsh") }), _jsx("button", { type: "button", className: "dsh-ssh-primary-button", onClick: () => setConnectionsOpen(true), children: t("file-transfer-workspace.newFtpConnection") })] })
135
+ return _jsxs("div", { className: "dsh-ssh-transfer-workspace", children: [_jsxs("header", { className: "dsh-ssh-transfer-header", children: [_jsxs("div", { children: [_jsx("h1", { children: t("client.fileTransfer") }), _jsx("p", { children: t("file-transfer-workspace.directStreamingTransfersBetweenFtpFtpsAndSftp") })] }), _jsxs("div", { className: "dsh-ssh-transfer-actions", children: [_jsxs("button", { type: "button", "data-ssh-interactive": "control", className: "dsh-ssh-secondary-button", onClick: () => setAccessOpen(true), children: [t("file-transfer-workspace.sessionAccess"), " ", access.value?.fileEndpointIds.length ?? 0] }), _jsx("button", { type: "button", "data-ssh-interactive": "control", className: "dsh-ssh-secondary-button", onClick: () => setConnectionsOpen(true), children: t("file-transfer-workspace.ftpManager") })] })] }), error && _jsxs("div", { className: "dsh-ssh-banner is-error", role: "alert", children: [_jsx("span", { children: error }), _jsx("button", { type: "button", onClick: () => setError(undefined), "aria-label": t("client.close"), children: _jsx(IconCloseOutline16, { size: 16 }) })] }), _jsxs("section", { ref: stageGlow.ref, onPointerMove: stageGlow.onPointerMove, onPointerLeave: stageGlow.onPointerLeave, className: "dsh-ssh-transfer-stage dsh-ssh-border-surface", children: [_jsxs("div", { className: "dsh-ssh-transfer-tabbar", role: "tablist", "aria-label": t("file-transfer-workspace.fileTransferTasksPage"), children: [_jsxs("div", { className: "dsh-ssh-transfer-tabs", children: [tabs.map(tab => _jsxs("span", { "data-ssh-interactive": "choice", className: `dsh-ssh-transfer-tab${tab.id === active?.id ? ' is-active' : ''}`, children: [_jsx("button", { type: "button", role: "tab", "aria-selected": tab.id === active?.id, onClick: () => setActiveTabId(tab.id), children: transferTabLabel(tab) }), tabs.length > 1 && _jsx("button", { type: "button", className: "is-close", "aria-label": t("file-transfer-workspace.close", [transferTabLabel(tab)]), onClick: () => closeTab(tab.id), children: _jsx(IconCloseOutline16, { size: 13 }) })] }, tab.id)), _jsx("button", { type: "button", "data-ssh-interactive": "control", className: "is-add", "aria-label": t("file-transfer-workspace.newTransferTasksPage"), onClick: addTab, children: _jsx(IconPlusOutline16, { size: 15 }) })] }), _jsx("div", { className: "dsh-ssh-pane-layout", "aria-label": t("file-transfer-workspace.panes"), children: [1, 2, 3, 4].map(count => _jsxs("button", { type: "button", "data-ssh-interactive": "choice", "aria-pressed": active?.panes.length === count, className: active?.panes.length === count ? 'is-active' : '', onClick: () => setPaneCount(count), children: [count, " ", t("file-transfer-workspace.panes2")] }, count)) })] }), endpoints.length === 0 ? _jsxs("div", { className: "dsh-ssh-transfer-empty", children: [_jsx(IconDataOutline16, { size: 24 }), _jsx("strong", { children: t("file-transfer-workspace.noFileConnectionsAvailableYet") }), _jsx("p", { children: t("file-transfer-workspace.createAnFtpFtpsConnectionOrAddAnSsh") }), _jsx("button", { type: "button", className: "dsh-ssh-primary-button", onClick: () => setConnectionsOpen(true), children: t("file-transfer-workspace.newFtpConnection") })] })
134
136
  : active && _jsx("div", { className: `dsh-ssh-transfer-panes has-${active.panes.length}`, children: active.panes.map((pane, index) => {
135
- const destination = active.panes[(index + 1) % active.panes.length];
137
+ const destination = active.panes.length > 1 ? active.panes[(index + 1) % active.panes.length] : undefined;
136
138
  return _jsx(FileTransferPane, { pane: pane, endpoints: endpoints, dragSource: dragSource, refreshRevision: directoryRevisions[directoryKey(pane.endpointId, pane.path)] ?? 0, ...destination === undefined ? {} : { destination }, onManageConnections: () => setConnectionsOpen(true), onDragSourceChange: setDragSource, onChange: patch => updateActive(tab => ({ ...tab, panes: tab.panes.map(item => item.id === pane.id ? { ...item, ...patch } : item) })), onTransfer: (paths, target) => { void transfer(pane.endpointId, pane.path, paths, target.endpointId, target.path); }, onExternalDrop: (source, destinationDirectory) => { void dropFiles(source, pane, destinationDirectory); } }, pane.id);
137
- }) }), _jsx(TransferQueue, { jobs: jobs, endpoints: endpoints, onCancel: async (id) => { try {
138
- await cancelFileTransfer(id);
139
- setJobs(current => current.map(job => job.id === id ? { ...job, state: 'cancelled' } : job));
140
- }
141
- catch (reason) {
142
- setError(errorMessage(reason));
143
- } }, onConflict: setConflictJob })] }), connectionsOpen && _jsx(FtpConnectionsDialog, { profiles: ftpProfiles, vaultEntries: vaultEntries, proxyEntries: proxyEntries, onClose: () => setConnectionsOpen(false), onChanged: () => { onProfilesChanged(); void refreshEndpoints(); } }), accessOpen && _jsx(FileAccessDialog, { endpoints: endpoints, access: access, onClose: () => setAccessOpen(false) }), conflictJob && _jsx(ConflictDialog, { job: conflictJob, onClose: () => setConflictJob(undefined), onRetry: async (conflictPolicy) => { try {
139
+ }) }), _jsx("div", { className: "dsh-ssh-transfer-queues", children: _jsx(TransferQueue, { jobs: jobs, endpoints: endpoints, onCancel: async (id) => { try {
140
+ await cancelFileTransfer(id);
141
+ setJobs(current => current.map(job => job.id === id ? { ...job, state: 'cancelled' } : job));
142
+ }
143
+ catch (reason) {
144
+ setError(errorMessage(reason));
145
+ } }, onConflict: setConflictJob }) })] }), connectionsOpen && _jsx(FtpConnectionsDialog, { profiles: ftpProfiles, vaultEntries: vaultEntries, proxyEntries: proxyEntries, onClose: () => setConnectionsOpen(false), onChanged: () => { onProfilesChanged(); void refreshEndpoints(); } }), accessOpen && _jsx(FileAccessDialog, { endpoints: endpoints, access: access, onClose: () => setAccessOpen(false) }), conflictJob && _jsx(ConflictDialog, { job: conflictJob, onClose: () => setConflictJob(undefined), onRetry: async (conflictPolicy) => { try {
144
146
  const next = await startFileTransfer({ ...conflictJob.request, conflictPolicy });
145
147
  setJobs(current => [next, ...current]);
146
148
  setConflictJob(undefined);
@@ -152,6 +154,10 @@ export function FileTransferWorkspace({ ftpProfiles, vaultEntries, proxyEntries,
152
154
  function FileTransferPane({ pane, endpoints, destination, dragSource, refreshRevision, onChange, onTransfer, onExternalDrop, onDragSourceChange, onManageConnections }) {
153
155
  const sshLocale = useSshLocale();
154
156
  const [openedFile, setOpenedFile] = useState();
157
+ const uploadInput = useRef(null);
158
+ const [uploading, setUploading] = useState(false);
159
+ const uploadLocation = useRef({ endpointId: pane.endpointId, path: pane.path });
160
+ uploadLocation.current = { endpointId: pane.endpointId, path: pane.path };
155
161
  const [view, setView] = useState();
156
162
  const [selected, setSelected] = useState([]);
157
163
  const [draftPath, setDraftPath] = useState(pane.path);
@@ -228,6 +234,32 @@ function FileTransferPane({ pane, endpoints, destination, dragSource, refreshRev
228
234
  };
229
235
  useEffect(() => { setOpenedFile(undefined); }, [endpoint?.id]);
230
236
  const submitPath = (event) => { event.preventDefault(); void openEntry(draftPath); };
237
+ const uploadFiles = async (files) => {
238
+ if (!endpoint || !view || uploading || files.length === 0)
239
+ return;
240
+ const target = { endpointId: endpoint.id, path: view.path };
241
+ setUploading(true);
242
+ setError(undefined);
243
+ const failures = [];
244
+ try {
245
+ for (const file of files) {
246
+ try {
247
+ await uploadFileEndpointFile(target.endpointId, target.path, file);
248
+ }
249
+ catch (reason) {
250
+ failures.push(`${file.name}: ${errorMessage(reason)}`);
251
+ }
252
+ }
253
+ if (uploadLocation.current.endpointId === target.endpointId && uploadLocation.current.path === target.path) {
254
+ await load(target.path);
255
+ if (failures.length)
256
+ setError(failures.join('\n'));
257
+ }
258
+ }
259
+ finally {
260
+ setUploading(false);
261
+ }
262
+ };
231
263
  const changeSort = (key) => {
232
264
  setSort(current => current.key === key ? { key, direction: current.direction === 'asc' ? 'desc' : 'asc' } : { key, direction: 'asc' });
233
265
  setScrollTop(0);
@@ -270,9 +302,9 @@ function FileTransferPane({ pane, endpoints, destination, dragSource, refreshRev
270
302
  setDragOver(false);
271
303
  setDirectoryDropTarget(undefined);
272
304
  } }, onDrop: drop, children: [_jsxs("header", { children: [_jsx("button", { type: "button", className: "dsh-ssh-pane-back", "aria-label": t("file-transfer-workspace.backToConnectionList"), onClick: showConnections, children: _jsx(IconChevronLeftOutline14, { size: 14 }) }), _jsxs("span", { className: "dsh-ssh-pane-endpoint", children: [_jsx("strong", { children: endpoint.name }), _jsx("small", { title: endpoint.address, children: endpoint.address })] }), _jsx("span", { className: `dsh-ssh-protocol-badge is-${endpoint.protocol}`, children: protocolShort(endpoint.protocol) })] }), _jsxs("form", { className: "dsh-ssh-file-pathbar", onSubmit: submitPath, children: [_jsx("button", { type: "button", "aria-label": t("file-transfer-workspace.parentDirectory"), disabled: view?.parent === null || loading, onClick: () => { if (view?.parent)
273
- void load(view.parent); }, children: _jsx(UpGlyph, {}) }), _jsx("button", { type: "button", "aria-label": t("file-transfer-workspace.refreshDirectory"), disabled: loading, onClick: () => { void load(); }, children: _jsx(RefreshGlyph, {}) }), _jsx("input", { "aria-label": t("file-transfer-workspace.remotePath"), value: draftPath, onChange: event => setDraftPath(event.target.value) }), _jsx("button", { type: "submit", disabled: loading, children: t("file-transfer-workspace.goTo") })] }), _jsxs("div", { className: "dsh-ssh-file-table", role: "grid", "aria-busy": loading, children: [_jsxs("div", { className: "dsh-ssh-file-table-head", role: "row", children: [_jsx(SortColumn, { label: t("client.name"), sortKey: "name", current: sort, onSort: changeSort }), _jsx(SortColumn, { label: t("file-transfer-workspace.size"), sortKey: "size", current: sort, onSort: changeSort }), _jsx(SortColumn, { label: t("file-transfer-workspace.modified"), sortKey: "modifiedAt", current: sort, onSort: changeSort }), _jsx("span", { role: "columnheader", "aria-label": t("file-transfer-workspace.actions") })] }), _jsx("div", { ref: bodyRef, className: "dsh-ssh-file-table-body", onScroll: event => { if (virtualized)
274
- setScrollTop(event.currentTarget.scrollTop); }, children: loading && !view ? _jsx("div", { className: "dsh-ssh-file-loading", children: t("file-transfer-workspace.readingDirectory") }) : error ? _jsxs("div", { className: "dsh-ssh-file-error", children: [_jsx("span", { children: error }), _jsx("button", { type: "button", onClick: () => { void load(); }, children: t("commands-panel.retry") })] }) : entries.length === 0 ? _jsx("div", { className: "dsh-ssh-table-empty", children: t("file-transfer-workspace.thisDirectoryIsEmpty") }) : _jsx("div", { className: virtualized ? 'dsh-ssh-file-virtual-list' : undefined, style: virtualized ? { height: `${entries.length * FILE_ROW_HEIGHT}px` } : undefined, children: visibleEntries.map((entry, offset) => _jsx(FileEntryRow, { entry: entry, paneId: pane.id, endpointId: endpoint.id, sourceDirectory: view?.path ?? pane.path, dragSource: dragSource, dropTarget: directoryDropTarget === entry.path, selected: selected.includes(entry.path), selectedPaths: selected, ...virtualized ? { style: { position: 'absolute', insetInline: 0, transform: `translateY(${(virtualStart + offset) * FILE_ROW_HEIGHT}px)` } } : {}, onDelete: () => setDeleteTarget([entry]), onDirectoryTarget: target => { setDragOver(false); setDirectoryDropTarget(target); }, onDropIntoDirectory: (payload, target) => { setDirectoryDropTarget(undefined); onDragSourceChange(undefined); onExternalDrop(payload, target); }, onDragSourceChange: onDragSourceChange, onSelect: additive => select(entry, additive), onOpen: () => { void openEntry(entry.path); } }, entry.path)) }) })] }), _jsxs("footer", { children: [_jsx("span", { children: selected.length > 0 ? t("file-transfer-workspace.selectedItems", [selected.length]) : t("file-transfer-workspace.items", [view?.entries.length ?? 0]) }), _jsx("span", { className: "dsh-ssh-file-pane-actions", children: _jsxs("button", { type: "button", "data-ssh-interactive": "control", className: "dsh-ssh-transfer-to-button", disabled: selected.length === 0 || destination === undefined || !destination.endpointId || loading, onClick: () => { if (destination?.endpointId)
275
- onTransfer(selected, destination); }, children: [t("file-transfer-workspace.sendToNextPane"), " ", _jsx("span", { "aria-hidden": "true", children: "\u2192" })] }) })] }), dragOver && _jsxs("div", { className: "dsh-ssh-file-drop-overlay", children: [_jsx("strong", { children: dragSource?.endpointId === pane.endpointId ? t("file-transfer-workspace.moveToThisDirectory") : t("file-transfer-workspace.copyToThisDirectory") }), _jsx("span", { children: view?.path ?? pane.path })] }), openedFile !== undefined && _jsx(Dialog, { className: "dsh-ssh-file-download-dialog", title: openedFile.name, subtitle: openedFile.path, onClose: () => setOpenedFile(undefined), children: _jsx("div", { className: "dsh-ssh-dialog-actions", children: _jsxs("a", { className: "dsh-ssh-secondary-button", href: fileEndpointDownloadUrl(endpoint.id, openedFile.path), download: true, children: [_jsx(DownloadGlyph, {}), _jsx("span", { children: t("file-transfer-workspace.download") })] }) }) }), deleteTarget !== undefined && _jsx(FileEntryDeleteDialog, { locationName: endpoint.name, locationKind: "remote", entries: deleteTarget, onClose: () => setDeleteTarget(undefined), onDelete: removeSelected })] });
305
+ void load(view.parent); }, children: _jsx(UpGlyph, {}) }), _jsx("button", { type: "button", "aria-label": t("file-transfer-workspace.refreshDirectory"), disabled: loading, onClick: () => { void load(); }, children: _jsx(RefreshGlyph, {}) }), _jsx("input", { "aria-label": t("file-transfer-workspace.remotePath"), value: draftPath, onChange: event => setDraftPath(event.target.value) }), _jsx("button", { type: "submit", disabled: loading, children: t("file-transfer-workspace.goTo") }), _jsx("button", { type: "button", disabled: loading || uploading || !view, "aria-label": t("local-transfer.uploadAction"), title: t("local-transfer.uploadAction"), onClick: () => uploadInput.current?.click(), children: uploading ? t("local-transfer.uploading") : t("browser-transfer.upload") }), _jsx("input", { ref: uploadInput, type: "file", multiple: true, hidden: true, onChange: event => { const files = Array.from(event.currentTarget.files ?? []); event.currentTarget.value = ''; void uploadFiles(files); } })] }), _jsxs("div", { className: "dsh-ssh-file-table", role: "grid", "aria-busy": loading, children: [_jsxs("div", { className: "dsh-ssh-file-table-head", role: "row", children: [_jsx(SortColumn, { label: t("client.name"), sortKey: "name", current: sort, onSort: changeSort }), _jsx(SortColumn, { label: t("file-transfer-workspace.size"), sortKey: "size", current: sort, onSort: changeSort }), _jsx(SortColumn, { label: t("file-transfer-workspace.modified"), sortKey: "modifiedAt", current: sort, onSort: changeSort }), _jsx("span", { role: "columnheader", "aria-label": t("file-transfer-workspace.actions") })] }), _jsx("div", { ref: bodyRef, className: "dsh-ssh-file-table-body", onScroll: event => { if (virtualized)
306
+ setScrollTop(event.currentTarget.scrollTop); }, children: loading && !view ? _jsx("div", { className: "dsh-ssh-file-loading", children: t("file-transfer-workspace.readingDirectory") }) : error ? _jsxs("div", { className: "dsh-ssh-file-error", children: [_jsx("span", { children: error }), _jsx("button", { type: "button", onClick: () => { void load(); }, children: t("commands-panel.retry") })] }) : entries.length === 0 ? _jsx("div", { className: "dsh-ssh-table-empty", children: t("file-transfer-workspace.thisDirectoryIsEmpty") }) : _jsx("div", { className: virtualized ? 'dsh-ssh-file-virtual-list' : undefined, style: virtualized ? { height: `${entries.length * FILE_ROW_HEIGHT}px` } : undefined, children: visibleEntries.map((entry, offset) => _jsx(FileEntryRow, { entry: entry, paneId: pane.id, endpointId: endpoint.id, sourceDirectory: view?.path ?? pane.path, dragSource: dragSource, dropTarget: directoryDropTarget === entry.path, selected: selected.includes(entry.path), selectedPaths: selected, ...virtualized ? { style: { position: 'absolute', insetInline: 0, transform: `translateY(${(virtualStart + offset) * FILE_ROW_HEIGHT}px)` } } : {}, onDelete: () => setDeleteTarget([entry]), onDirectoryTarget: target => { setDragOver(false); setDirectoryDropTarget(target); }, onDropIntoDirectory: (payload, target) => { setDirectoryDropTarget(undefined); onDragSourceChange(undefined); onExternalDrop(payload, target); }, onDragSourceChange: onDragSourceChange, onSelect: additive => select(entry, additive), onOpen: () => { void openEntry(entry.path); } }, entry.path)) }) })] }), _jsxs("footer", { children: [_jsx("span", { children: selected.length > 0 ? t("file-transfer-workspace.selectedItems", [selected.length]) : t("file-transfer-workspace.items", [view?.entries.length ?? 0]) }), destination !== undefined && _jsx("span", { className: "dsh-ssh-file-pane-actions", children: _jsxs("button", { type: "button", "data-ssh-interactive": "control", className: "dsh-ssh-transfer-to-button", disabled: selected.length === 0 || !destination.endpointId || loading, onClick: () => { if (destination.endpointId)
307
+ onTransfer(selected, destination); }, children: [t("file-transfer-workspace.sendToNextPane"), " ", _jsx("span", { "aria-hidden": "true", children: "\u2192" })] }) })] }), dragOver && _jsxs("div", { className: "dsh-ssh-file-drop-overlay", children: [_jsx("strong", { children: dragSource?.endpointId === pane.endpointId ? t("file-transfer-workspace.moveToThisDirectory") : t("file-transfer-workspace.copyToThisDirectory") }), _jsx("span", { children: view?.path ?? pane.path })] }), openedFile !== undefined && _jsx(Dialog, { variant: "confirmation", className: "dsh-ssh-file-download-dialog", title: openedFile.name, subtitle: openedFile.path, onClose: () => setOpenedFile(undefined), children: _jsx("div", { className: "dsh-ssh-dialog-actions", children: _jsxs("a", { onClick: trackDownloadClick, className: "dsh-ssh-secondary-button", href: fileEndpointDownloadUrl(endpoint.id, openedFile.path), download: true, children: [_jsx(DownloadGlyph, {}), _jsx("span", { children: t("file-transfer-workspace.download") })] }) }) }), deleteTarget !== undefined && _jsx(FileEntryDeleteDialog, { locationName: endpoint.name, locationKind: "remote", entries: deleteTarget, onClose: () => setDeleteTarget(undefined), onDelete: removeSelected })] });
276
308
  }
277
309
  function SortColumn({ label, sortKey, current, onSort }) {
278
310
  useSshLocale();
@@ -317,29 +349,40 @@ function FileEntryRow({ entry, paneId, endpointId, sourceDirectory, dragSource,
317
349
  event.dataTransfer.dropEffect = dragSource?.endpointId === endpointId ? 'move' : 'copy';
318
350
  onDirectoryTarget(entry.path);
319
351
  } }, onDragLeave: event => { if (dropTarget && !event.currentTarget.contains(event.relatedTarget))
320
- onDirectoryTarget(undefined); }, onDrop: dropIntoDirectory, children: [_jsxs("span", { children: [_jsx(FileGlyph, { directory: directory }), _jsx("i", { title: entry.name, children: entry.name })] }), _jsx("span", { children: directory ? '—' : formatBytes(entry.size) }), _jsx("span", { children: entry.modifiedAt > 0 ? new Date(entry.modifiedAt).toLocaleString() : '—' }), _jsxs("span", { className: "dsh-ssh-file-row-actions dsh-ssh-context-action", children: [_jsx("a", { className: "dsh-ssh-file-row-download", href: fileEndpointDownloadUrl(endpointId, entry.path), download: true, draggable: false, "aria-label": t("file-transfer-workspace.downloadLocally", [entry.name]), title: entry.kind === 'directory' ? t("file-transfer-workspace.packageAndDownloadLocally", [entry.name]) : t("file-transfer-workspace.downloadLocally", [entry.name]), onClick: event => event.stopPropagation(), children: _jsx(DownloadGlyph, {}) }), _jsx("button", { type: "button", className: "dsh-ssh-file-row-delete", draggable: false, "aria-label": t("client.delete2", [entry.name]), title: t("client.delete2", [entry.name]), onClick: event => { event.stopPropagation(); onDelete(); }, children: _jsx(IconTrashOutline16, { size: 14 }) })] })] });
352
+ onDirectoryTarget(undefined); }, onDrop: dropIntoDirectory, children: [_jsxs("span", { children: [_jsx(FileGlyph, { directory: directory }), _jsx("i", { title: entry.name, children: entry.name })] }), _jsx("span", { children: directory ? '—' : formatBytes(entry.size) }), _jsx("span", { title: entry.modifiedAt > 0 ? new Date(entry.modifiedAt).toLocaleString() : entry.modifiedAtText, children: entry.modifiedAt > 0 ? new Date(entry.modifiedAt).toLocaleString() : entry.modifiedAtText || '—' }), _jsxs("span", { className: "dsh-ssh-file-row-actions dsh-ssh-context-action", children: [_jsx("a", { className: "dsh-ssh-file-row-download", href: fileEndpointDownloadUrl(endpointId, entry.path), download: directory ? `${entry.name}.tar` : entry.name, draggable: false, "aria-label": t("file-transfer-workspace.downloadLocally", [entry.name]), title: entry.kind === 'directory' ? t("file-transfer-workspace.packageAndDownloadLocally", [entry.name]) : t("file-transfer-workspace.downloadLocally", [entry.name]), onClick: trackDownloadClick, children: _jsx(DownloadGlyph, {}) }), _jsx("button", { type: "button", className: "dsh-ssh-file-row-delete", draggable: false, "aria-label": t("client.delete2", [entry.name]), title: t("client.delete2", [entry.name]), onClick: event => { event.stopPropagation(); onDelete(); }, children: _jsx(IconTrashOutline16, { size: 14 }) })] })] });
321
353
  }
322
354
  function TransferQueue({ jobs, endpoints, onCancel, onConflict }) {
323
355
  useSshLocale();
324
- const [open, setOpen] = useState(true);
356
+ const browserJobs = useBrowserTransferTasks();
325
357
  const active = jobs.filter(job => job.state === 'queued' || job.state === 'scanning' || job.state === 'transferring');
326
- return _jsxs("section", { className: `dsh-ssh-transfer-queue${open ? ' is-open' : ''}`, children: [_jsxs("button", { type: "button", className: "dsh-ssh-transfer-queue-heading", "aria-expanded": open, title: open ? t("file-transfer-workspace.collapseTransferTasks") : t("file-transfer-workspace.expandTransferTasks"), onClick: () => setOpen(value => !value), children: [_jsxs("span", { children: [_jsx("strong", { children: t("file-transfer-workspace.transferTasks") }), _jsx("small", { children: active.length > 0 ? t("file-transfer-workspace.inProgress", [active.length]) : jobs.length > 0 ? t("file-transfer-workspace.recentTasks") : t("file-transfer-workspace.noTasksYet") })] }), _jsx("span", { className: "dsh-ssh-transfer-queue-disclosure", "aria-hidden": "true", children: _jsx(IconChevronDownOutline14, { size: 14 }) })] }), open && _jsx("div", { className: "dsh-ssh-transfer-job-list", children: jobs.length === 0 ? _jsx("p", { children: t("file-transfer-workspace.dragFromOnePaneToAnotherOrSelectFiles") }) : jobs.slice(0, 12).map(job => {
327
- const progress = job.totalBytes > 0 ? Math.min(100, job.transferredBytes / job.totalBytes * 100) : job.state === 'completed' ? 100 : 0;
328
- return _jsxs("article", { children: [_jsx("span", { className: `dsh-ssh-transfer-state is-${job.state}`, "aria-hidden": "true" }), _jsxs("span", { className: "dsh-ssh-transfer-job-copy", children: [_jsxs("strong", { children: [remoteLabel(job.request.sourcePaths), " ", _jsx("i", { children: "\u2192" }), " ", endpointName(endpoints, job.request.destinationEndpointId)] }), _jsx("small", { children: job.error ?? jobLabel(job, progress) }), _jsx("span", { className: "dsh-ssh-transfer-job-time", children: jobTimeLabel(job) }), _jsx("span", { className: "dsh-ssh-transfer-progress", children: _jsx("i", { style: { transform: `scaleX(${progress / 100})` } }) })] }), (job.state === 'queued' || job.state === 'scanning' || job.state === 'transferring') ? _jsx("button", { type: "button", className: "dsh-ssh-icon-button", "aria-label": t("file-transfer-workspace.cancelTransfer"), onClick: () => { void onCancel(job.id); }, children: _jsx(IconCloseOutline16, { size: 15 }) }) : job.state === 'failed' && job.error?.includes('destination already contains') ? _jsx("button", { type: "button", className: "dsh-ssh-job-resolve", onClick: () => onConflict(job), children: t("file-transfer-workspace.handle") }) : null] }, job.id);
329
- }) })] });
358
+ const combined = [
359
+ ...jobs.map(job => ({ kind: 'remote', job, active: active.includes(job) })),
360
+ ...browserJobs.map(job => ({ kind: 'browser', job, active: isBrowserTransferActive(job) })),
361
+ ].sort((a, b) => Number(b.active) - Number(a.active) || b.job.createdAt - a.job.createdAt);
362
+ return _jsx(TransferTaskList, { activeCount: active.length + browserJobs.filter(isBrowserTransferActive).length, count: combined.length, children: combined.length === 0 ? _jsx("p", { children: t("file-transfer-workspace.dragFromOnePaneToAnotherOrSelectFiles") }) : combined.map(item => {
363
+ if (item.kind === 'browser')
364
+ return _jsx(BrowserTransferTask, { job: item.job }, 'browser-' + item.job.id);
365
+ const job = item.job;
366
+ const progress = job.totalBytes > 0 ? Math.min(100, job.transferredBytes / job.totalBytes * 100) : job.state === 'completed' ? 100 : 0;
367
+ return _jsxs("article", { children: [_jsx("span", { className: `dsh-ssh-transfer-state is-${job.state}`, "aria-hidden": "true" }), _jsxs("span", { className: "dsh-ssh-transfer-job-copy", children: [_jsxs("strong", { children: [remoteLabel(job.request.sourcePaths), " ", _jsx("i", { children: "\u2192" }), " ", endpointName(endpoints, job.request.destinationEndpointId)] }), _jsx("small", { children: job.error ?? jobLabel(job, progress) }), _jsx("span", { className: "dsh-ssh-transfer-job-time", children: jobTimeLabel(job) }), _jsx("span", { className: "dsh-ssh-transfer-progress", children: _jsx("i", { style: { transform: `scaleX(${progress / 100})` } }) })] }), (job.state === 'queued' || job.state === 'scanning' || job.state === 'transferring') ? _jsx("button", { type: "button", className: "dsh-ssh-icon-button", "aria-label": t("file-transfer-workspace.cancelTransfer"), onClick: () => { void onCancel(job.id); }, children: _jsx(IconCloseOutline16, { size: 15 }) }) : job.state === 'failed' && job.error?.includes('destination already contains') ? _jsx("button", { type: "button", className: "dsh-ssh-job-resolve", onClick: () => onConflict(job), children: t("file-transfer-workspace.handle") }) : null] }, job.id);
368
+ }) });
330
369
  }
331
370
  function ConflictDialog({ job, onClose, onRetry }) {
332
371
  useSshLocale();
333
372
  const [policy, setPolicy] = useState('rename');
334
373
  const [saving, setSaving] = useState(false);
335
- return _jsxs(Dialog, { className: "dsh-ssh-transfer-conflict-dialog", title: t("file-transfer-workspace.aFileWithTheSameNameExistsAtThe"), subtitle: t("file-transfer-workspace.chooseHowToHandleThisBatchOfTransfers"), onClose: onClose, children: [_jsxs("div", { className: "dsh-ssh-transfer-conflict-copy", children: [_jsx("strong", { children: remoteLabel(job.request.sourcePaths) }), _jsx("span", { children: job.request.destinationDirectory })] }), _jsx("div", { className: "dsh-ssh-conflict-options", children: [['rename', t("file-transfer-workspace.autoRename"), t("file-transfer-workspace.keepBothFilesAppendANumberToTheName")], ['skip', t("file-transfer-workspace.skipFilesWithTheSameName"), t("file-transfer-workspace.continueTransferringTheRemainingItems")], ['overwrite', t("file-transfer-workspace.overwriteTheTargetFiles"), t("file-transfer-workspace.replaceTheDestinationContentWithTheSourceContent")]].map(option => _jsxs("button", { type: "button", "data-ssh-interactive": "choice", "aria-pressed": policy === option[0], className: policy === option[0] ? 'is-active' : '', onClick: () => setPolicy(option[0]), children: [_jsx("i", { "aria-hidden": "true" }), _jsxs("span", { children: [_jsx("strong", { children: option[1] }), _jsx("small", { children: option[2] })] })] }, option[0])) }), _jsxs("div", { className: "dsh-ssh-dialog-actions", children: [_jsx("button", { type: "button", className: "dsh-ssh-secondary-button", "data-ssh-dialog-close": true, onClick: onClose, children: t("client.cancel") }), _jsx("button", { type: "button", className: "dsh-ssh-primary-button", disabled: saving, onClick: () => { setSaving(true); void onRetry(policy).finally(() => setSaving(false)); }, children: saving ? t("file-transfer-workspace.reQueuing") : t("file-transfer-workspace.continueTransfer") })] })] });
374
+ return _jsxs(Dialog, { variant: "confirmation", className: "dsh-ssh-transfer-conflict-dialog", title: t("file-transfer-workspace.aFileWithTheSameNameExistsAtThe"), subtitle: t("file-transfer-workspace.chooseHowToHandleThisBatchOfTransfers"), onClose: onClose, children: [_jsxs("div", { className: "dsh-ssh-transfer-conflict-copy", children: [_jsx("strong", { children: remoteLabel(job.request.sourcePaths) }), _jsx("span", { children: job.request.destinationDirectory })] }), _jsx("div", { className: "dsh-ssh-conflict-options", children: [['rename', t("file-transfer-workspace.autoRename"), t("file-transfer-workspace.keepBothFilesAppendANumberToTheName")], ['skip', t("file-transfer-workspace.skipFilesWithTheSameName"), t("file-transfer-workspace.continueTransferringTheRemainingItems")], ['overwrite', t("file-transfer-workspace.overwriteTheTargetFiles"), t("file-transfer-workspace.replaceTheDestinationContentWithTheSourceContent")]].map(option => _jsxs("button", { type: "button", "data-ssh-interactive": "choice", "aria-pressed": policy === option[0], className: policy === option[0] ? 'is-active' : '', onClick: () => setPolicy(option[0]), children: [_jsx("i", { "aria-hidden": "true" }), _jsxs("span", { children: [_jsx("strong", { children: option[1] }), _jsx("small", { children: option[2] })] })] }, option[0])) }), _jsxs("div", { className: "dsh-ssh-dialog-actions", children: [_jsx("button", { type: "button", className: "dsh-ssh-secondary-button", "data-ssh-dialog-close": true, onClick: onClose, children: t("client.cancel") }), _jsx("button", { type: "button", className: "dsh-ssh-primary-button", disabled: saving, onClick: () => { setSaving(true); void onRetry(policy).finally(() => setSaving(false)); }, children: saving ? t("file-transfer-workspace.reQueuing") : t("file-transfer-workspace.continueTransfer") })] })] });
336
375
  }
337
376
  function FileAccessDialog({ endpoints, access, onClose }) {
338
377
  useSshLocale();
339
378
  const value = access.value;
340
379
  const mounted = endpoints.filter(endpoint => value?.fileEndpointIds.includes(endpoint.id)).length;
341
380
  const toggle = (id) => access.setFileEndpoints(value?.fileEndpointIds.includes(id) ? value.fileEndpointIds.filter(item => item !== id) : [...(value?.fileEndpointIds ?? []), id]);
342
- return _jsxs(Dialog, { className: "dsh-ssh-file-access-dialog", title: t("file-transfer-workspace.sessionAccess2"), subtitle: t("file-transfer-workspace.manageTheRemoteFileConnectionsAvailableToThisSession"), onClose: onClose, children: [_jsxs("div", { className: "dsh-ssh-file-access-body dsh-ssh-scroll-surface", children: [access.error && _jsx("p", { className: "dsh-ssh-inline-error", role: "alert", children: access.error }), _jsxs("section", { className: "dsh-ssh-file-access-section", "aria-labelledby": "dsh-ssh-file-access-connections", children: [_jsxs("header", { children: [_jsxs("span", { children: [_jsx("strong", { id: "dsh-ssh-file-access-connections", children: t("file-transfer-workspace.fileConnections") }), _jsx("small", { children: t("file-transfer-workspace.letTheCurrentSessionBrowseSelectedFtpFtpsOr") })] }), _jsxs("i", { children: [mounted.toString().padStart(2, '0'), " / ", endpoints.length.toString().padStart(2, '0')] })] }), _jsx("div", { className: "dsh-ssh-file-access-list", children: access.loading ? _jsx(FileAccessSkeleton, {}) : endpoints.length === 0 ? _jsxs("div", { className: "dsh-ssh-file-access-empty", children: [_jsx("strong", { children: t("file-transfer-workspace.noConnectionsAvailable") }), _jsx("small", { children: t("file-transfer-workspace.addAnFtpInFileTransferFirstOrAdd") })] }) : endpoints.map(endpoint => _jsx(FileAccessEndpoint, { endpoint: endpoint, enabled: value?.fileEndpointIds.includes(endpoint.id) ?? false, onToggle: () => toggle(endpoint.id) }, endpoint.id)) })] }), _jsxs("section", { className: "dsh-ssh-file-access-section is-options", "aria-labelledby": "dsh-ssh-file-access-policy", children: [_jsx("header", { children: _jsxs("span", { children: [_jsx("strong", { id: "dsh-ssh-file-access-policy", children: t("file-transfer-workspace.accessPolicy") }), _jsx("small", { children: t("file-transfer-workspace.filePermissionsAreSeparateFromSshCommandAndTerminal") })] }) }), _jsxs("div", { className: "dsh-ssh-file-access-options", children: [_jsxs("div", { className: "dsh-ssh-file-access-policy-row", children: [_jsxs("span", { children: [_jsx("strong", { children: t("file-transfer-workspace.fileOperationPermissions") }), _jsx("small", { children: t("file-transfer-workspace.browseModeDoesNotExposeCrossEndpointTransferTools") })] }), _jsx(FilePermissionPicker, { value: value?.filePermission ?? 'browse', onChange: access.setFilePermission })] }), _jsxs("div", { className: "dsh-ssh-file-access-policy-row", children: [_jsxs("span", { children: [_jsx("strong", { children: t("file-transfer-workspace.confirmBeforeTransfer") }), _jsx("small", { children: t("file-transfer-workspace.requireDshApprovalWhenTheAiStartsATransfer") })] }), _jsx(FileApprovalSwitch, { checked: value?.requireFileApproval ?? true, onChange: access.setRequireFileApproval })] })] })] })] }), _jsxs("footer", { className: "dsh-ssh-file-access-footer", children: [_jsxs("span", { className: access.saving ? 'is-saving' : 'is-saved', children: [_jsx("i", { "aria-hidden": "true" }), access.saving ? t("file-transfer-workspace.savingChanges") : t("file-transfer-workspace.autosaves", [mounted > 0 ? t("file-transfer-workspace.authorizedConnections", [mounted]) : t("file-transfer-workspace.noAuthorizedConnections")])] }), _jsx("button", { type: "button", className: "dsh-ssh-primary-button", "data-ssh-dialog-close": true, disabled: access.saving, onClick: onClose, children: access.saving ? t("client.saving2") : t("file-transfer-workspace.done") })] })] });
381
+ return _jsxs(Dialog, { className: "dsh-ssh-file-access-dialog", title: t("file-transfer-workspace.sessionAccess2"), subtitle: t("file-transfer-workspace.manageTheRemoteFileConnectionsAvailableToThisSession"), onClose: onClose, children: [_jsxs("div", { className: "dsh-ssh-file-access-body dsh-ssh-scroll-surface", children: [access.error && _jsx("p", { className: "dsh-ssh-inline-error", role: "alert", children: access.error }), _jsxs("section", { className: "dsh-ssh-file-access-section", "aria-labelledby": "dsh-ssh-file-access-connections", children: [_jsxs("header", { children: [_jsxs("span", { children: [_jsx("strong", { id: "dsh-ssh-file-access-connections", children: t("file-transfer-workspace.fileConnections") }), _jsx("small", { children: t("file-transfer-workspace.letTheCurrentSessionBrowseSelectedFtpFtpsOr") })] }), _jsxs("i", { children: [mounted.toString().padStart(2, '0'), " / ", endpoints.length.toString().padStart(2, '0')] })] }), _jsx("div", { className: "dsh-ssh-file-access-list", children: access.loading ? _jsx(FileAccessSkeleton, {}) : endpoints.length === 0 ? _jsxs("div", { className: "dsh-ssh-file-access-empty", children: [_jsx("strong", { children: t("file-transfer-workspace.noConnectionsAvailable") }), _jsx("small", { children: t("file-transfer-workspace.addAnFtpInFileTransferFirstOrAdd") })] }) : endpoints.map(endpoint => _jsx(FileAccessEndpoint, { endpoint: endpoint, enabled: value?.fileEndpointIds.includes(endpoint.id) ?? false, onToggle: () => toggle(endpoint.id) }, endpoint.id)) })] }), _jsxs("section", { className: "dsh-ssh-file-access-section is-options", "aria-labelledby": "dsh-ssh-file-access-policy", children: [_jsx("header", { children: _jsxs("span", { children: [_jsx("strong", { id: "dsh-ssh-file-access-policy", children: t("file-transfer-workspace.accessPolicy") }), _jsx("small", { children: t("file-transfer-workspace.filePermissionsAreSeparateFromSshCommandAndTerminal") })] }) }), _jsxs("div", { className: "dsh-ssh-file-access-options", children: [_jsxs("div", { className: "dsh-ssh-file-access-policy-row", children: [_jsxs("span", { children: [_jsx("strong", { children: t("file-transfer-workspace.fileOperationPermissions") }), _jsx("small", { children: t("file-transfer-workspace.browseModeDoesNotExposeCrossEndpointTransferTools") })] }), _jsx(FilePermissionPicker, { value: value?.filePermission ?? 'browse', onChange: access.setFilePermission })] }), _jsxs("div", { className: "dsh-ssh-file-access-policy-row", children: [_jsxs("span", { children: [_jsx("strong", { children: t("file-transfer-workspace.confirmBeforeTransfer") }), _jsx("small", { children: t("file-transfer-workspace.requireDshApprovalWhenTheAiStartsATransfer") })] }), _jsx(FileApprovalSwitch, { checked: value?.requireFileApproval ?? true, onChange: access.setRequireFileApproval })] }), ['upload', 'download'].map(direction => {
382
+ const enabled = (direction === 'upload' ? value?.allowLocalUpload : value?.allowLocalDownload) === true;
383
+ const label = direction === 'upload' ? t("local-transfer.allowUpload") : t("local-transfer.allowDownload");
384
+ return _jsxs("div", { className: "dsh-ssh-file-access-policy-row", children: [_jsxs("span", { children: [_jsx("strong", { children: label }), _jsx("small", { children: direction === 'upload' ? t("local-transfer.uploadScope") : t("local-transfer.downloadScope") })] }), _jsxs("button", { type: "button", className: `dsh-ssh-file-approval-switch${enabled ? ' is-active' : ''}`, role: "switch", "aria-label": label, "aria-checked": enabled, disabled: access.loading || !value, onClick: () => access.setLocalFileAccess(direction, !enabled), children: [_jsx("i", { "aria-hidden": "true", children: _jsx("b", {}) }), _jsx("span", { children: enabled ? t("file-transfer-workspace.authorized") : t("file-transfer-workspace.notAuthorized") })] })] }, direction);
385
+ })] })] })] }), _jsxs("footer", { className: "dsh-ssh-file-access-footer", children: [_jsxs("span", { className: access.saving ? 'is-saving' : 'is-saved', children: [_jsx("i", { "aria-hidden": "true" }), access.saving ? t("file-transfer-workspace.savingChanges") : t("file-transfer-workspace.autosaves", [mounted > 0 ? t("file-transfer-workspace.authorizedConnections", [mounted]) : t("file-transfer-workspace.noAuthorizedConnections")])] }), _jsx("button", { type: "button", className: "dsh-ssh-primary-button", "data-ssh-dialog-close": true, disabled: access.saving, onClick: onClose, children: access.saving ? t("client.saving2") : t("file-transfer-workspace.done") })] })] });
343
386
  }
344
387
  function FileAccessEndpoint({ endpoint, enabled, onToggle }) {
345
388
  useSshLocale();
@@ -359,7 +402,8 @@ function FileApprovalSwitch({ checked, onChange }) {
359
402
  }
360
403
  function restoreTabs() {
361
404
  try {
362
- const value = JSON.parse(localStorage.getItem(STORAGE_KEY) ?? '[]');
405
+ const stored = localStorage.getItem(STORAGE_KEY);
406
+ const value = JSON.parse(stored ?? localStorage.getItem('dsh-ssh:file-transfer:tabs:v2') ?? '[]');
363
407
  if (!Array.isArray(value))
364
408
  return [createTab('transfer-1', 1)];
365
409
  const tabs = value.slice(0, 8).flatMap((candidate) => {
@@ -376,7 +420,7 @@ function restoreTabs() {
376
420
  ? [{ id: state.id, endpointId: '', path: '/' }]
377
421
  : [];
378
422
  });
379
- return panes.length >= 2 ? [{ id: tab.id, name: tab.name.slice(0, 40), panes }] : [];
423
+ return panes.length >= 1 ? [{ id: tab.id, name: tab.name.slice(0, 40), panes: stored === null ? panes.slice(0, 1) : panes }] : [];
380
424
  });
381
425
  if (tabs.length > 0)
382
426
  return tabs;
@@ -384,7 +428,7 @@ function restoreTabs() {
384
428
  catch { }
385
429
  return [createTab('transfer-1', 1)];
386
430
  }
387
- function createTab(id, index) { return { id, name: `任务 ${index}`, panes: [createPane(`${id}-a`), createPane(`${id}-b`)] }; }
431
+ function createTab(id, index) { return { id, name: `任务 ${index}`, panes: [createPane(`${id}-a`)] }; }
388
432
  function transferTabLabel(tab) {
389
433
  // Keep the existing persisted representation; only translate generated display labels.
390
434
  const number = /^任务 (\d+)$/.exec(tab.name)?.[1];