@electerm/electerm-react 5.5.25 → 5.5.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/client/common/bookmark-schemas.js +4 -0
- package/client/common/constants.js +3 -0
- package/client/common/create-title.jsx +5 -1
- package/client/common/default-setting.js +2 -0
- package/client/common/parse-quick-connect.js +1 -0
- package/client/common/parse-vv.js +119 -25
- package/client/common/parse-widget-log.js +57 -0
- package/client/common/vv-to-tab.js +120 -0
- package/client/components/ai/agent-tool-call-card.jsx +26 -7
- package/client/components/ai/agent.js +205 -45
- package/client/components/ai/ai-auto-compress.js +119 -0
- package/client/components/ai/ai-chat-history-item.jsx +133 -84
- package/client/components/ai/ai-chat.jsx +135 -30
- package/client/components/ai/ai-compress.js +95 -0
- package/client/components/ai/ai-config-props.js +12 -1
- package/client/components/ai/ai-config.jsx +43 -16
- package/client/components/ai/ai-config.styl +20 -0
- package/client/components/ai/ai-context-indicator.jsx +83 -0
- package/client/components/ai/ai-context.js +272 -0
- package/client/components/ai/ai-output.jsx +5 -6
- package/client/components/ai/ai-presets.js +1 -16
- package/client/components/ai/ai.styl +105 -6
- package/client/components/ai/copy-icon.jsx +31 -0
- package/client/components/batch-op/batch-op-runner.jsx +1 -0
- package/client/components/bookmark-form/bookmark-schema.js +1 -0
- package/client/components/bookmark-form/common/ssh-agent.jsx +31 -21
- package/client/components/bookmark-form/common/vv-file-field.jsx +3 -2
- package/client/components/bookmark-form/config/spice.js +21 -0
- package/client/components/bookmark-form/config/ssh.js +1 -0
- package/client/components/bookmark-form/fix-bookmark-default.js +1 -0
- package/client/components/file-transfer/transfer.jsx +3 -119
- package/client/components/footer/cmd-history.jsx +279 -65
- package/client/components/footer/cmd-history.styl +68 -1
- package/client/components/footer/footer-entry.jsx +7 -1
- package/client/components/layout/layout.jsx +12 -19
- package/client/components/main/main.jsx +15 -1
- package/client/components/quick-commands/qm.styl +58 -0
- package/client/components/quick-commands/quick-commands-box.jsx +180 -83
- package/client/components/quick-commands/quick-commands-select.jsx +11 -0
- package/client/components/session/session-control.jsx +35 -2
- package/client/components/session/session-control.styl +5 -0
- package/client/components/session/session.jsx +13 -0
- package/client/components/setting-panel/setting-common.jsx +2 -0
- package/client/components/setting-panel/tab-widgets.jsx +27 -7
- package/client/components/sftp/file-item.jsx +0 -13
- package/client/components/side-panel-r/side-panel-r.jsx +11 -4
- package/client/components/sidebar/bookmark-select.jsx +14 -2
- package/client/components/sidebar/history-item.jsx +20 -4
- package/client/components/sidebar/history.jsx +7 -0
- package/client/components/terminal/mixins/term-touch.js +18 -0
- package/client/components/tree-list/tree-list.jsx +31 -3
- package/client/components/widgets/widget-control.jsx +4 -2
- package/client/components/widgets/widget-instance-detail.jsx +370 -0
- package/client/components/widgets/widget-instance.jsx +30 -13
- package/client/components/widgets/widget-instances.jsx +3 -1
- package/client/components/widgets/widgets-list.jsx +8 -0
- package/client/components/widgets/widgets.styl +100 -0
- package/client/store/common.js +200 -54
- package/client/store/init-state.js +15 -0
- package/client/store/load-data.js +73 -0
- package/client/store/store.js +6 -0
- package/client/store/watch.js +3 -1
- package/client/store/widgets.js +23 -5
- package/package.json +1 -1
- package/client/components/file-transfer/zip.js +0 -42
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { CopyOutlined } from '@ant-design/icons'
|
|
2
|
+
import { copy } from '../../common/clipboard'
|
|
3
|
+
|
|
4
|
+
const e = window.translate
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Always visible copy icon, shared by AI output and agent tool call blocks
|
|
8
|
+
* @param {Object} props
|
|
9
|
+
* @param {string} props.text - text to copy
|
|
10
|
+
* @param {string} props.className - extra class for positioning
|
|
11
|
+
* @param {string} props.title - tooltip title
|
|
12
|
+
*/
|
|
13
|
+
export default function CopyIcon ({ text, className = '', title }) {
|
|
14
|
+
function handleCopy (evt) {
|
|
15
|
+
// agent tool card header toggles expanded state on click
|
|
16
|
+
evt.stopPropagation()
|
|
17
|
+
copy(text)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!text) {
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<CopyOutlined
|
|
26
|
+
className={`copy-icon pointer iblock${className ? ' ' + className : ''}`}
|
|
27
|
+
onClick={handleCopy}
|
|
28
|
+
title={title || e('copy')}
|
|
29
|
+
/>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
@@ -172,6 +172,7 @@ export default class BatchOpRunner extends Component {
|
|
|
172
172
|
enableSsh: p.enableSsh !== false,
|
|
173
173
|
useSshAgent: p.useSshAgent !== false,
|
|
174
174
|
sshAgent: p.sshAgent || '',
|
|
175
|
+
agentForward: p.agentForward || false,
|
|
175
176
|
term: p.term || 'xterm-256color',
|
|
176
177
|
encode: p.encode || 'utf8',
|
|
177
178
|
envLang: p.envLang || 'en_US.UTF-8',
|
|
@@ -20,6 +20,7 @@ const bookmarkSchema = {
|
|
|
20
20
|
connectionHoppings: 'array - connection hopping definitions',
|
|
21
21
|
useSshAgent: 'boolean - use SSH agent, default is true',
|
|
22
22
|
sshAgent: 'string - ssh agent path',
|
|
23
|
+
agentForward: 'boolean - enable SSH agent forwarding (like ssh -A, needed to hop through a jump/bastion host), requires SSH agent, default is false',
|
|
23
24
|
serverHostKey: 'array - server host key algorithms',
|
|
24
25
|
cipher: 'array - cipher list',
|
|
25
26
|
compress: 'array - compression algorithms (zlib@openssh.com, zlib, none)',
|
|
@@ -9,26 +9,36 @@ const e = window.translate
|
|
|
9
9
|
|
|
10
10
|
export default function SshAgent () {
|
|
11
11
|
return (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
12
|
+
<>
|
|
13
|
+
<FormItem
|
|
14
|
+
{...formItemLayout}
|
|
15
|
+
label={e('useSshAgent')}
|
|
16
|
+
>
|
|
17
|
+
<Space align='center'>
|
|
18
|
+
<FormItem
|
|
19
|
+
name='useSshAgent'
|
|
20
|
+
valuePropName='checked'
|
|
21
|
+
noStyle
|
|
22
|
+
>
|
|
23
|
+
<SwitchLabel />
|
|
24
|
+
</FormItem>
|
|
25
|
+
<FormItem
|
|
26
|
+
name='sshAgent'
|
|
27
|
+
noStyle
|
|
28
|
+
>
|
|
29
|
+
<Input placeholder={e('SSH Agent Path')} />
|
|
30
|
+
</FormItem>
|
|
31
|
+
<HelpIcon link='https://github.com/electerm/electerm/wiki/ssh-agent' />
|
|
32
|
+
</Space>
|
|
33
|
+
</FormItem>
|
|
34
|
+
<FormItem
|
|
35
|
+
{...formItemLayout}
|
|
36
|
+
label={e('agentForward')}
|
|
37
|
+
name='agentForward'
|
|
38
|
+
valuePropName='checked'
|
|
39
|
+
>
|
|
40
|
+
<SwitchLabel />
|
|
41
|
+
</FormItem>
|
|
42
|
+
</>
|
|
33
43
|
)
|
|
34
44
|
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* bookmark form - load a .vv (virt-viewer) connection file
|
|
3
3
|
*
|
|
4
4
|
* Registered as field type 'vvFile' in ./fields.jsx and used by
|
|
5
|
-
* ../config/spice.js. Fills host/port/password/title/proxy
|
|
6
|
-
* reports anything it could not honour instead of dropping
|
|
5
|
+
* ../config/spice.js. Fills host/port/password/title/proxy/tls/ca/hostSubject
|
|
6
|
+
* from the file and reports anything it could not honour instead of dropping
|
|
7
|
+
* it silently.
|
|
7
8
|
*/
|
|
8
9
|
import { useState } from 'react'
|
|
9
10
|
import {
|
|
@@ -12,6 +12,9 @@ const spiceConfig = {
|
|
|
12
12
|
initValues: (props) => {
|
|
13
13
|
return createBaseInitValues(props, terminalSpiceType, {
|
|
14
14
|
port: 5900,
|
|
15
|
+
tls: false,
|
|
16
|
+
ca: '',
|
|
17
|
+
hostSubject: '',
|
|
15
18
|
viewOnly: false,
|
|
16
19
|
scaleViewport: true,
|
|
17
20
|
connectionHoppings: [],
|
|
@@ -29,6 +32,24 @@ const spiceConfig = {
|
|
|
29
32
|
{ type: 'vvFile', name: '__vv__', label: '' },
|
|
30
33
|
{ type: 'input', name: 'host', label: () => e('host'), rules: [{ required: true, message: e('host') + ' required' }] },
|
|
31
34
|
commonFields.port,
|
|
35
|
+
{ type: 'switch', name: 'tls', label: 'TLS', valuePropName: 'checked' },
|
|
36
|
+
{
|
|
37
|
+
type: 'textarea',
|
|
38
|
+
name: 'ca',
|
|
39
|
+
label: 'CA certificate (PEM)',
|
|
40
|
+
props: {
|
|
41
|
+
autoSize: { minRows: 2, maxRows: 8 },
|
|
42
|
+
placeholder: '-----BEGIN CERTIFICATE-----'
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
type: 'input',
|
|
47
|
+
name: 'hostSubject',
|
|
48
|
+
label: 'Certificate subject',
|
|
49
|
+
props: {
|
|
50
|
+
placeholder: 'CN=pve1,O=Proxmox Virtual Environment'
|
|
51
|
+
}
|
|
52
|
+
},
|
|
32
53
|
{ type: 'switch', name: 'viewOnly', label: () => e('viewOnly'), valuePropName: 'checked' },
|
|
33
54
|
{ type: 'switch', name: 'scaleViewport', label: () => e('scaleViewport'), valuePropName: 'checked' },
|
|
34
55
|
{ type: 'profileItem', name: '__profile__', label: '', profileFilter: d => !isEmpty(d.spice) },
|
|
@@ -12,13 +12,6 @@ import {
|
|
|
12
12
|
import resolve from '../../common/resolve'
|
|
13
13
|
import sanitizeFilename from '../../common/sanitize-filename'
|
|
14
14
|
import { refsTransfers, refsStatic, refs } from '../common/ref'
|
|
15
|
-
import {
|
|
16
|
-
zipCmd,
|
|
17
|
-
unzipCmd,
|
|
18
|
-
rmCmd,
|
|
19
|
-
mvCmd,
|
|
20
|
-
mkdirCmd
|
|
21
|
-
} from './zip'
|
|
22
15
|
import './transfer.styl'
|
|
23
16
|
|
|
24
17
|
const { assign } = Object
|
|
@@ -279,9 +272,7 @@ export default class TransportAction extends Component {
|
|
|
279
272
|
typeFrom,
|
|
280
273
|
toFile = {}
|
|
281
274
|
} = transfer
|
|
282
|
-
const toPath = transfer.
|
|
283
|
-
? transfer.toPath
|
|
284
|
-
: this.newPath || transfer.toPath
|
|
275
|
+
const toPath = this.newPath || transfer.toPath
|
|
285
276
|
const fromFile = transfer.fromFile || this.fromFile
|
|
286
277
|
const fromMode = fromFile.mode
|
|
287
278
|
const transferType = typeFrom === typeMap.local ? transferTypeMap.upload : transferTypeMap.download
|
|
@@ -408,12 +399,11 @@ export default class TransportAction extends Component {
|
|
|
408
399
|
toPath
|
|
409
400
|
} = this.props.transfer
|
|
410
401
|
this.oldPath = toPath
|
|
411
|
-
const { newPath
|
|
402
|
+
const { newPath } = this.handleRename(toPath, typeTo === typeMap.remote)
|
|
412
403
|
this.update({
|
|
413
404
|
toPath: newPath
|
|
414
405
|
})
|
|
415
406
|
this.newPath = newPath
|
|
416
|
-
this.newName = newName
|
|
417
407
|
}
|
|
418
408
|
|
|
419
409
|
const { typeFrom, typeTo } = this.props.transfer
|
|
@@ -423,120 +413,14 @@ export default class TransportAction extends Component {
|
|
|
423
413
|
this.startTransfer()
|
|
424
414
|
}
|
|
425
415
|
|
|
426
|
-
zipTransferFolder = async () => {
|
|
427
|
-
const {
|
|
428
|
-
transfer
|
|
429
|
-
} = this.props
|
|
430
|
-
const {
|
|
431
|
-
fromPath,
|
|
432
|
-
typeFrom
|
|
433
|
-
} = transfer
|
|
434
|
-
const toPath = this.oldPath || transfer.toPath
|
|
435
|
-
let p
|
|
436
|
-
let isFromRemote
|
|
437
|
-
if (typeFrom === typeMap.local) {
|
|
438
|
-
isFromRemote = false
|
|
439
|
-
p = await window.fs.zipFolder(fromPath)
|
|
440
|
-
} else {
|
|
441
|
-
isFromRemote = true
|
|
442
|
-
const terminalId = refs.get('sftp-' + this.tabId)?.terminalId
|
|
443
|
-
p = await zipCmd(terminalId, fromPath)
|
|
444
|
-
}
|
|
445
|
-
this.zipSrc = p
|
|
446
|
-
const { name } = getFolderFromFilePath(p, isFromRemote)
|
|
447
|
-
const { path } = getFolderFromFilePath(toPath, !isFromRemote)
|
|
448
|
-
const nTo = resolve(path, name)
|
|
449
|
-
this.zipPath = nTo
|
|
450
|
-
const newTrans1 = {
|
|
451
|
-
...copy(transfer),
|
|
452
|
-
toPath: nTo,
|
|
453
|
-
fromPath: p
|
|
454
|
-
}
|
|
455
|
-
this.transferFile(newTrans1, this.unzipFile)
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
unzipFile = async () => {
|
|
459
|
-
const { transfer } = this.props
|
|
460
|
-
const {
|
|
461
|
-
typeTo
|
|
462
|
-
} = transfer
|
|
463
|
-
const toPath = this.zipPath
|
|
464
|
-
const fromPath = this.zipSrc
|
|
465
|
-
const isToRemote = typeTo === typeMap.remote
|
|
466
|
-
const {
|
|
467
|
-
path,
|
|
468
|
-
name,
|
|
469
|
-
targetPath
|
|
470
|
-
} = this.buildUnzipPath(transfer)
|
|
471
|
-
const {
|
|
472
|
-
newName,
|
|
473
|
-
terminalId
|
|
474
|
-
} = this
|
|
475
|
-
if (isToRemote) {
|
|
476
|
-
if (newName) {
|
|
477
|
-
await mkdirCmd(terminalId, path)
|
|
478
|
-
}
|
|
479
|
-
await unzipCmd(terminalId, toPath, path)
|
|
480
|
-
if (newName) {
|
|
481
|
-
const mvFrom = resolve(path, name)
|
|
482
|
-
const mvTo = resolve(targetPath, newName)
|
|
483
|
-
await mvCmd(terminalId, mvFrom, mvTo)
|
|
484
|
-
}
|
|
485
|
-
} else {
|
|
486
|
-
if (newName) {
|
|
487
|
-
await window.fs.mkdir(path)
|
|
488
|
-
}
|
|
489
|
-
await window.fs.unzipFile(toPath, path)
|
|
490
|
-
if (newName) {
|
|
491
|
-
const mvFrom = resolve(path, name)
|
|
492
|
-
const mvTo = resolve(targetPath, newName)
|
|
493
|
-
await window.fs.mv(mvFrom, mvTo)
|
|
494
|
-
}
|
|
495
|
-
}
|
|
496
|
-
await rmCmd(terminalId, !isToRemote ? fromPath : toPath)
|
|
497
|
-
await window.fs.rmrf(!isToRemote ? toPath : fromPath)
|
|
498
|
-
if (newName) {
|
|
499
|
-
if (isToRemote) {
|
|
500
|
-
await rmCmd(terminalId, path)
|
|
501
|
-
} else {
|
|
502
|
-
await window.fs.rmrf(path)
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
this.onEnd()
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
buildUnzipPath = (transfer) => {
|
|
509
|
-
const {
|
|
510
|
-
typeTo
|
|
511
|
-
} = transfer
|
|
512
|
-
const isToRemote = typeTo === typeMap.remote
|
|
513
|
-
const toPath = this.oldPath || transfer.toPath
|
|
514
|
-
const {
|
|
515
|
-
newName
|
|
516
|
-
} = this
|
|
517
|
-
const { path } = getFolderFromFilePath(toPath, isToRemote)
|
|
518
|
-
const oldName = getFolderFromFilePath(toPath, isToRemote).name
|
|
519
|
-
const np = newName
|
|
520
|
-
? resolve(path, 'temp-' + newName)
|
|
521
|
-
: path
|
|
522
|
-
return {
|
|
523
|
-
targetPath: path,
|
|
524
|
-
path: np,
|
|
525
|
-
name: oldName
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
416
|
startTransfer = async () => {
|
|
530
|
-
const { fromFile = this.fromFile
|
|
417
|
+
const { fromFile = this.fromFile } = this.props.transfer
|
|
531
418
|
if (!fromFile) {
|
|
532
419
|
return
|
|
533
420
|
}
|
|
534
421
|
if (!fromFile.isDirectory) {
|
|
535
422
|
return this.transferFile()
|
|
536
423
|
}
|
|
537
|
-
if (zip) {
|
|
538
|
-
return this.zipTransferFolder()
|
|
539
|
-
}
|
|
540
424
|
if (!this.isFtp) {
|
|
541
425
|
return this.transferFile()
|
|
542
426
|
} else {
|