@electerm/electerm-react 1.90.8 → 1.91.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/common/resolve.js +6 -4
- package/client/components/file-transfer/transfer.jsx +132 -9
- package/client/components/file-transfer/zip.js +42 -0
- package/client/components/sftp/file-item.jsx +17 -1
- package/client/components/sys-menu/icons-map.jsx +6 -2
- package/client/components/sys-menu/layoout-changer.jsx +57 -0
- package/client/components/sys-menu/menu-btn.jsx +5 -0
- package/client/components/sys-menu/sys-menu.jsx +3 -1
- package/client/components/terminal/terminal.jsx +39 -3
- package/client/components/terminal/terminal.styl +3 -0
- package/package.json +1 -1
package/client/common/resolve.js
CHANGED
|
@@ -5,23 +5,25 @@
|
|
|
5
5
|
* @return {String}
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
export default (basePath, nameOrDot)
|
|
8
|
+
export default function resolve (basePath, nameOrDot) {
|
|
9
9
|
const hasWinDrive = (path) => /^[a-zA-Z]:/.test(path)
|
|
10
10
|
const isWin = basePath.includes('\\') || nameOrDot.includes('\\') || hasWinDrive(basePath) || hasWinDrive(nameOrDot)
|
|
11
11
|
const sep = isWin ? '\\' : '/'
|
|
12
|
-
// Handle Windows drive letters (with or without initial slash)
|
|
13
12
|
if (/^[a-zA-Z]:/.test(nameOrDot)) {
|
|
14
13
|
return nameOrDot.replace(/^\//, '').replace(/\//g, sep)
|
|
15
14
|
}
|
|
16
|
-
// Handle absolute paths
|
|
17
15
|
if (nameOrDot.startsWith('/')) {
|
|
18
16
|
return nameOrDot.replace(/\\/g, sep)
|
|
19
17
|
}
|
|
20
18
|
if (nameOrDot === '..') {
|
|
19
|
+
const baseEndsWithSep = basePath.endsWith(sep)
|
|
21
20
|
const parts = basePath.split(sep)
|
|
22
21
|
if (parts.length > 1) {
|
|
23
22
|
parts.pop()
|
|
24
|
-
|
|
23
|
+
if (isWin && parts.length === 1) {
|
|
24
|
+
return baseEndsWithSep ? '/' : parts.join(sep)
|
|
25
|
+
}
|
|
26
|
+
return parts.join(sep) || '/'
|
|
25
27
|
}
|
|
26
28
|
return '/'
|
|
27
29
|
}
|
|
@@ -12,6 +12,13 @@ import {
|
|
|
12
12
|
} from '../sftp/file-read'
|
|
13
13
|
import resolve from '../../common/resolve'
|
|
14
14
|
import { refsTransfers, refsStatic, refs } from '../common/ref'
|
|
15
|
+
import {
|
|
16
|
+
zipCmd,
|
|
17
|
+
unzipCmd,
|
|
18
|
+
rmCmd,
|
|
19
|
+
mvCmd,
|
|
20
|
+
mkdirCmd
|
|
21
|
+
} from './zip'
|
|
15
22
|
import './transfer.styl'
|
|
16
23
|
|
|
17
24
|
const { assign } = Object
|
|
@@ -24,13 +31,15 @@ export default class TransportAction extends Component {
|
|
|
24
31
|
transferBatch = '',
|
|
25
32
|
tabId
|
|
26
33
|
} = props.transfer
|
|
34
|
+
const sftp = refs.get('sftp-' + tabId)
|
|
27
35
|
this.id = `tr-${transferBatch}-${id}`
|
|
28
36
|
this.tabId = tabId
|
|
29
37
|
refsTransfers.add(this.id, this)
|
|
30
38
|
this.total = 0
|
|
31
39
|
this.transferred = 0
|
|
32
40
|
this.currentProgress = 1
|
|
33
|
-
this.isFtp =
|
|
41
|
+
this.isFtp = sftp?.type === 'ftp'
|
|
42
|
+
this.terminalId = sftp?.terminalId
|
|
34
43
|
}
|
|
35
44
|
|
|
36
45
|
componentDidMount () {
|
|
@@ -233,7 +242,7 @@ export default class TransportAction extends Component {
|
|
|
233
242
|
|
|
234
243
|
// Check if it's a copy operation to the same path
|
|
235
244
|
if (fromPath === toPath && operation === fileOperationsMap.cp) {
|
|
236
|
-
finalToPath = this.handleRename(toPath, typeFrom === typeMap.remote)
|
|
245
|
+
finalToPath = this.handleRename(toPath, typeFrom === typeMap.remote).newPath
|
|
237
246
|
transfer.toPath = finalToPath
|
|
238
247
|
this.update({
|
|
239
248
|
toPath: finalToPath
|
|
@@ -256,13 +265,15 @@ export default class TransportAction extends Component {
|
|
|
256
265
|
})
|
|
257
266
|
}
|
|
258
267
|
|
|
259
|
-
transferFile = async (transfer = this.props.transfer) => {
|
|
268
|
+
transferFile = async (transfer = this.props.transfer, onEnd = this.onEnd) => {
|
|
260
269
|
const {
|
|
261
270
|
fromPath,
|
|
262
271
|
typeFrom,
|
|
263
272
|
toFile = {}
|
|
264
273
|
} = transfer
|
|
265
|
-
const toPath =
|
|
274
|
+
const toPath = transfer.zip
|
|
275
|
+
? transfer.toPath
|
|
276
|
+
: this.newPath || transfer.toPath
|
|
266
277
|
const fromFile = transfer.fromFile || this.fromFile
|
|
267
278
|
const fromMode = fromFile.mode
|
|
268
279
|
const transferType = typeFrom === typeMap.local ? transferTypeMap.upload : transferTypeMap.download
|
|
@@ -281,7 +292,7 @@ export default class TransportAction extends Component {
|
|
|
281
292
|
options: { mode },
|
|
282
293
|
onData: this.onData,
|
|
283
294
|
onError: this.onError,
|
|
284
|
-
onEnd
|
|
295
|
+
onEnd
|
|
285
296
|
})
|
|
286
297
|
}
|
|
287
298
|
|
|
@@ -384,23 +395,132 @@ export default class TransportAction extends Component {
|
|
|
384
395
|
typeTo,
|
|
385
396
|
toPath
|
|
386
397
|
} = this.props.transfer
|
|
387
|
-
|
|
398
|
+
this.oldPath = toPath
|
|
399
|
+
const { newPath, newName } = this.handleRename(toPath, typeTo === typeMap.remote)
|
|
388
400
|
this.update({
|
|
389
401
|
toPath: newPath
|
|
390
402
|
})
|
|
391
403
|
this.newPath = newPath
|
|
404
|
+
this.newName = newName
|
|
392
405
|
}
|
|
393
406
|
|
|
394
407
|
this.startTransfer()
|
|
395
408
|
}
|
|
396
409
|
|
|
410
|
+
zipTransferFolder = async () => {
|
|
411
|
+
const {
|
|
412
|
+
transfer
|
|
413
|
+
} = this.props
|
|
414
|
+
const {
|
|
415
|
+
fromPath,
|
|
416
|
+
typeFrom
|
|
417
|
+
} = transfer
|
|
418
|
+
const toPath = this.oldPath || transfer.toPath
|
|
419
|
+
let p
|
|
420
|
+
let isFromRemote
|
|
421
|
+
if (typeFrom === typeMap.local) {
|
|
422
|
+
isFromRemote = false
|
|
423
|
+
p = await fs.zipFolder(fromPath)
|
|
424
|
+
} else {
|
|
425
|
+
isFromRemote = true
|
|
426
|
+
const terminalId = refs.get('sftp-' + this.tabId)?.terminalId
|
|
427
|
+
p = await zipCmd(terminalId, fromPath)
|
|
428
|
+
}
|
|
429
|
+
this.zipSrc = p
|
|
430
|
+
const { name } = getFolderFromFilePath(p, isFromRemote)
|
|
431
|
+
const { path } = getFolderFromFilePath(toPath, !isFromRemote)
|
|
432
|
+
const nTo = resolve(path, name)
|
|
433
|
+
this.zipPath = nTo
|
|
434
|
+
const newTrans1 = {
|
|
435
|
+
...copy(transfer),
|
|
436
|
+
toPath: nTo,
|
|
437
|
+
fromPath: p
|
|
438
|
+
}
|
|
439
|
+
this.transferFile(newTrans1, this.unzipFile)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
unzipFile = async () => {
|
|
443
|
+
const { transfer } = this.props
|
|
444
|
+
const {
|
|
445
|
+
typeTo
|
|
446
|
+
} = transfer
|
|
447
|
+
const toPath = this.zipPath
|
|
448
|
+
const fromPath = this.zipSrc
|
|
449
|
+
const isToRemote = typeTo === typeMap.remote
|
|
450
|
+
const {
|
|
451
|
+
path,
|
|
452
|
+
name,
|
|
453
|
+
targetPath
|
|
454
|
+
} = this.buildUnzipPath(transfer)
|
|
455
|
+
const {
|
|
456
|
+
newName,
|
|
457
|
+
terminalId
|
|
458
|
+
} = this
|
|
459
|
+
if (isToRemote) {
|
|
460
|
+
if (newName) {
|
|
461
|
+
await mkdirCmd(terminalId, path)
|
|
462
|
+
}
|
|
463
|
+
await unzipCmd(terminalId, toPath, path)
|
|
464
|
+
if (newName) {
|
|
465
|
+
const mvFrom = resolve(path, name)
|
|
466
|
+
const mvTo = resolve(targetPath, newName)
|
|
467
|
+
await mvCmd(terminalId, mvFrom, mvTo)
|
|
468
|
+
}
|
|
469
|
+
} else {
|
|
470
|
+
if (newName) {
|
|
471
|
+
await fs.mkdir(path)
|
|
472
|
+
}
|
|
473
|
+
await fs.unzipFile(toPath, path)
|
|
474
|
+
if (newName) {
|
|
475
|
+
const mvFrom = resolve(path, name)
|
|
476
|
+
const mvTo = resolve(targetPath, newName)
|
|
477
|
+
await fs.mv(mvFrom, mvTo)
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
await rmCmd(terminalId, !isToRemote ? fromPath : toPath)
|
|
481
|
+
await fs.rmrf(!isToRemote ? toPath : fromPath)
|
|
482
|
+
if (newName) {
|
|
483
|
+
if (isToRemote) {
|
|
484
|
+
await rmCmd(terminalId, path)
|
|
485
|
+
} else {
|
|
486
|
+
await fs.rmrf(path)
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
this.onEnd()
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
buildUnzipPath = (transfer) => {
|
|
493
|
+
const {
|
|
494
|
+
typeTo
|
|
495
|
+
} = transfer
|
|
496
|
+
const isToRemote = typeTo === typeMap.remote
|
|
497
|
+
const toPath = this.oldPath || transfer.toPath
|
|
498
|
+
const {
|
|
499
|
+
newName
|
|
500
|
+
} = this
|
|
501
|
+
const { path } = getFolderFromFilePath(toPath, isToRemote)
|
|
502
|
+
const oldName = getFolderFromFilePath(toPath, isToRemote).name
|
|
503
|
+
const np = newName
|
|
504
|
+
? resolve(path, 'temp-' + newName)
|
|
505
|
+
: path
|
|
506
|
+
return {
|
|
507
|
+
targetPath: path,
|
|
508
|
+
path: np,
|
|
509
|
+
name: oldName
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
397
513
|
startTransfer = async () => {
|
|
398
|
-
const { fromFile = this.fromFile } = this.props.transfer
|
|
514
|
+
const { fromFile = this.fromFile, zip } = this.props.transfer
|
|
399
515
|
|
|
400
516
|
if (!fromFile.isDirectory) {
|
|
401
517
|
return this.transferFile()
|
|
402
518
|
}
|
|
403
|
-
|
|
519
|
+
if (zip) {
|
|
520
|
+
return this.zipTransferFolder()
|
|
521
|
+
} else {
|
|
522
|
+
await this.transferFolderRecursive()
|
|
523
|
+
}
|
|
404
524
|
this.onEnd({
|
|
405
525
|
transferred: this.transferred,
|
|
406
526
|
size: this.total
|
|
@@ -415,7 +535,10 @@ export default class TransportAction extends Component {
|
|
|
415
535
|
handleRename = (fromPath, isRemote) => {
|
|
416
536
|
const { path, base, ext } = getFolderFromFilePath(fromPath, isRemote)
|
|
417
537
|
const newName = `${base}(rename-${generate()})${ext ? '.' + ext : ''}`
|
|
418
|
-
return
|
|
538
|
+
return {
|
|
539
|
+
newPath: resolve(path, newName),
|
|
540
|
+
newName
|
|
541
|
+
}
|
|
419
542
|
}
|
|
420
543
|
|
|
421
544
|
onFolderData = (transferred) => {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* zip/unzip remote files
|
|
3
|
+
* should only support linux server
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { runCmd } from '../terminal/terminal-apis'
|
|
7
|
+
import { getFolderFromFilePath } from '../sftp/file-read'
|
|
8
|
+
import resolve from '../../common/resolve'
|
|
9
|
+
import generate from '../../common/uid'
|
|
10
|
+
|
|
11
|
+
const isRemote = true
|
|
12
|
+
const temp = '/tmp'
|
|
13
|
+
|
|
14
|
+
export async function zipCmd (pid, filePath) {
|
|
15
|
+
// tar -czf bin.tar bin
|
|
16
|
+
const id = generate()
|
|
17
|
+
const { path, name } = getFolderFromFilePath(filePath, isRemote)
|
|
18
|
+
const np = resolve(temp, `electerm-${id}.tar`)
|
|
19
|
+
const cmd = `tar -C "${path}" -cf "${np}" "${name}"`
|
|
20
|
+
await runCmd(pid, cmd)
|
|
21
|
+
return np
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function unzipCmd (pid, from, to) {
|
|
25
|
+
const cmd = `tar -xf "${from}" -C "${to}"`
|
|
26
|
+
return runCmd(pid, cmd)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function rmCmd (pid, path) {
|
|
30
|
+
const cmd = `rm -rf "${path}"`
|
|
31
|
+
return runCmd(pid, cmd)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function mvCmd (pid, from, to) {
|
|
35
|
+
const cmd = `mv "${from}" "${to}"`
|
|
36
|
+
return runCmd(pid, cmd)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function mkdirCmd (pid, p) {
|
|
40
|
+
const cmd = `mkdir "${p}"`
|
|
41
|
+
return runCmd(pid, cmd)
|
|
42
|
+
}
|
|
@@ -795,9 +795,12 @@ export default class FileSection extends React.Component {
|
|
|
795
795
|
this.props.addTransferList(all)
|
|
796
796
|
}
|
|
797
797
|
|
|
798
|
-
transfer = async () => {
|
|
798
|
+
transfer = async (mapper) => {
|
|
799
799
|
const { file } = this.state
|
|
800
800
|
const arr = await this.getTransferList(file)
|
|
801
|
+
if (mapper) {
|
|
802
|
+
arr.forEach(mapper)
|
|
803
|
+
}
|
|
801
804
|
this.props.addTransferList(arr)
|
|
802
805
|
}
|
|
803
806
|
|
|
@@ -835,6 +838,12 @@ export default class FileSection extends React.Component {
|
|
|
835
838
|
this.transfer()
|
|
836
839
|
}
|
|
837
840
|
|
|
841
|
+
zipAndTransfer = async () => {
|
|
842
|
+
this.transfer(transfer => {
|
|
843
|
+
transfer.zip = true
|
|
844
|
+
})
|
|
845
|
+
}
|
|
846
|
+
|
|
838
847
|
newFile = () => {
|
|
839
848
|
return this.newItem(false)
|
|
840
849
|
}
|
|
@@ -1000,6 +1009,13 @@ export default class FileSection extends React.Component {
|
|
|
1000
1009
|
icon: iconType,
|
|
1001
1010
|
text: transferText
|
|
1002
1011
|
})
|
|
1012
|
+
if (isDirectory && !this.props.isFtp) {
|
|
1013
|
+
res.push({
|
|
1014
|
+
func: 'zipAndTransfer',
|
|
1015
|
+
icon: 'FileZipOutlined',
|
|
1016
|
+
text: e('compressAndTransfer')
|
|
1017
|
+
})
|
|
1018
|
+
}
|
|
1003
1019
|
}
|
|
1004
1020
|
if (!isDirectory && isRealFile && isLocal) {
|
|
1005
1021
|
res.push({
|
|
@@ -22,7 +22,9 @@ import {
|
|
|
22
22
|
FolderAddOutlined,
|
|
23
23
|
InfoCircleOutlined,
|
|
24
24
|
LockOutlined,
|
|
25
|
-
ReloadOutlined
|
|
25
|
+
ReloadOutlined,
|
|
26
|
+
FileZipOutlined,
|
|
27
|
+
AppstoreOutlined
|
|
26
28
|
} from '@ant-design/icons'
|
|
27
29
|
import IconHolder from './icon-holder'
|
|
28
30
|
|
|
@@ -48,5 +50,7 @@ export default {
|
|
|
48
50
|
FolderAddOutlined,
|
|
49
51
|
InfoCircleOutlined,
|
|
50
52
|
LockOutlined,
|
|
51
|
-
ReloadOutlined
|
|
53
|
+
ReloadOutlined,
|
|
54
|
+
FileZipOutlined,
|
|
55
|
+
AppstoreOutlined
|
|
52
56
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SingleIcon,
|
|
3
|
+
TwoColumnsIcon,
|
|
4
|
+
ThreeColumnsIcon,
|
|
5
|
+
TwoRowsIcon,
|
|
6
|
+
ThreeRowsIcon,
|
|
7
|
+
Grid2x2Icon,
|
|
8
|
+
TwoRowsRightIcon,
|
|
9
|
+
TwoColumnsBottomIcon
|
|
10
|
+
} from '../icons/split-icons'
|
|
11
|
+
import {
|
|
12
|
+
splitMapDesc
|
|
13
|
+
} from '../../common/constants'
|
|
14
|
+
|
|
15
|
+
const e = window.translate
|
|
16
|
+
|
|
17
|
+
export default function LayoutChanger (props) {
|
|
18
|
+
const getLayoutIcon = (layout) => {
|
|
19
|
+
const iconMaps = {
|
|
20
|
+
single: SingleIcon,
|
|
21
|
+
twoColumns: TwoColumnsIcon,
|
|
22
|
+
threeColumns: ThreeColumnsIcon,
|
|
23
|
+
twoRows: TwoRowsIcon,
|
|
24
|
+
threeRows: ThreeRowsIcon,
|
|
25
|
+
grid2x2: Grid2x2Icon,
|
|
26
|
+
twoRowsRight: TwoRowsRightIcon,
|
|
27
|
+
twoColumnsBottom: TwoColumnsBottomIcon
|
|
28
|
+
}
|
|
29
|
+
return iconMaps[layout]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const handleChangeLayout = ({ key }) => {
|
|
33
|
+
window.store.setLayout(key)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const items = Object.keys(splitMapDesc).map((t) => {
|
|
37
|
+
const v = splitMapDesc[t]
|
|
38
|
+
const Icon = getLayoutIcon(v)
|
|
39
|
+
return (
|
|
40
|
+
<div
|
|
41
|
+
key={t}
|
|
42
|
+
className='sub-context-menu-item'
|
|
43
|
+
onClick={() => handleChangeLayout({ key: t })}
|
|
44
|
+
>
|
|
45
|
+
<span>
|
|
46
|
+
<Icon /> {e(v)}
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div className='sub-context-menu bookmarks-sub-context-menu'>
|
|
54
|
+
{items}
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
57
|
+
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
import { noop } from 'lodash-es'
|
|
11
11
|
import History from './history'
|
|
12
12
|
import Bookmark from './boomarks'
|
|
13
|
+
import Layout from './layoout-changer'
|
|
13
14
|
import Tabs from './tabs'
|
|
14
15
|
import Zoom from './zoom'
|
|
15
16
|
import icons from './icons-map'
|
|
@@ -22,7 +23,8 @@ export default class ContextMenu extends PureComponent {
|
|
|
22
23
|
History,
|
|
23
24
|
Bookmark,
|
|
24
25
|
Tabs,
|
|
25
|
-
Zoom
|
|
26
|
+
Zoom,
|
|
27
|
+
Layout
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
onClick = (e, item) => {
|
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
Spin,
|
|
13
13
|
Button,
|
|
14
14
|
Dropdown,
|
|
15
|
-
message
|
|
15
|
+
message,
|
|
16
|
+
Modal
|
|
16
17
|
} from 'antd'
|
|
17
18
|
import classnames from 'classnames'
|
|
18
19
|
import './terminal.styl'
|
|
@@ -28,7 +29,7 @@ import {
|
|
|
28
29
|
zmodemTransferPackSize
|
|
29
30
|
} from '../../common/constants.js'
|
|
30
31
|
import deepCopy from 'json-deep-copy'
|
|
31
|
-
import { readClipboardAsync, copy } from '../../common/clipboard.js'
|
|
32
|
+
import { readClipboardAsync, readClipboard, copy } from '../../common/clipboard.js'
|
|
32
33
|
import { FitAddon } from '@xterm/addon-fit'
|
|
33
34
|
import AttachAddon from './attach-addon-custom.js'
|
|
34
35
|
import { SearchAddon } from '@xterm/addon-search'
|
|
@@ -289,7 +290,39 @@ clear\r`
|
|
|
289
290
|
this.tryInsertSelected()
|
|
290
291
|
}
|
|
291
292
|
|
|
293
|
+
pasteTextTooLong = () => {
|
|
294
|
+
if (window.et.isWebApp) {
|
|
295
|
+
return false
|
|
296
|
+
}
|
|
297
|
+
const text = readClipboard()
|
|
298
|
+
return text.length > 500
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
askUserConfirm = () => {
|
|
302
|
+
Modal.confirm({
|
|
303
|
+
title: e('paste'),
|
|
304
|
+
content: (
|
|
305
|
+
<div>
|
|
306
|
+
<p>{e('paste')}:</p>
|
|
307
|
+
<div className='paste-text'>
|
|
308
|
+
{readClipboard()}
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
),
|
|
312
|
+
okText: e('ok'),
|
|
313
|
+
cancelText: e('cancel'),
|
|
314
|
+
onOk: () => this.onPaste(true),
|
|
315
|
+
onCancel: Modal.destroyAll
|
|
316
|
+
})
|
|
317
|
+
}
|
|
318
|
+
|
|
292
319
|
pasteShortcut = (e) => {
|
|
320
|
+
if (this.pasteTextTooLong()) {
|
|
321
|
+
this.askUserConfirm()
|
|
322
|
+
e.preventDefault()
|
|
323
|
+
e.stopPropagation()
|
|
324
|
+
return false
|
|
325
|
+
}
|
|
293
326
|
if (isMac) {
|
|
294
327
|
return true
|
|
295
328
|
}
|
|
@@ -706,8 +739,11 @@ clear\r`
|
|
|
706
739
|
return this.props.tab?.host
|
|
707
740
|
}
|
|
708
741
|
|
|
709
|
-
onPaste = async () => {
|
|
742
|
+
onPaste = async (skipTextLengthCheck) => {
|
|
710
743
|
let selected = await readClipboardAsync()
|
|
744
|
+
if (!skipTextLengthCheck && selected.length > 500) {
|
|
745
|
+
return this.askUserConfirm()
|
|
746
|
+
}
|
|
711
747
|
if (isWin && this.isRemote()) {
|
|
712
748
|
selected = selected.replace(/\r\n/g, '\n')
|
|
713
749
|
}
|