@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.
Files changed (66) hide show
  1. package/README.md +5 -5
  2. package/client/common/bookmark-schemas.js +4 -0
  3. package/client/common/constants.js +3 -0
  4. package/client/common/create-title.jsx +5 -1
  5. package/client/common/default-setting.js +2 -0
  6. package/client/common/parse-quick-connect.js +1 -0
  7. package/client/common/parse-vv.js +119 -25
  8. package/client/common/parse-widget-log.js +57 -0
  9. package/client/common/vv-to-tab.js +120 -0
  10. package/client/components/ai/agent-tool-call-card.jsx +26 -7
  11. package/client/components/ai/agent.js +205 -45
  12. package/client/components/ai/ai-auto-compress.js +119 -0
  13. package/client/components/ai/ai-chat-history-item.jsx +133 -84
  14. package/client/components/ai/ai-chat.jsx +135 -30
  15. package/client/components/ai/ai-compress.js +95 -0
  16. package/client/components/ai/ai-config-props.js +12 -1
  17. package/client/components/ai/ai-config.jsx +43 -16
  18. package/client/components/ai/ai-config.styl +20 -0
  19. package/client/components/ai/ai-context-indicator.jsx +83 -0
  20. package/client/components/ai/ai-context.js +272 -0
  21. package/client/components/ai/ai-output.jsx +5 -6
  22. package/client/components/ai/ai-presets.js +1 -16
  23. package/client/components/ai/ai.styl +105 -6
  24. package/client/components/ai/copy-icon.jsx +31 -0
  25. package/client/components/batch-op/batch-op-runner.jsx +1 -0
  26. package/client/components/bookmark-form/bookmark-schema.js +1 -0
  27. package/client/components/bookmark-form/common/ssh-agent.jsx +31 -21
  28. package/client/components/bookmark-form/common/vv-file-field.jsx +3 -2
  29. package/client/components/bookmark-form/config/spice.js +21 -0
  30. package/client/components/bookmark-form/config/ssh.js +1 -0
  31. package/client/components/bookmark-form/fix-bookmark-default.js +1 -0
  32. package/client/components/file-transfer/transfer.jsx +3 -119
  33. package/client/components/footer/cmd-history.jsx +279 -65
  34. package/client/components/footer/cmd-history.styl +68 -1
  35. package/client/components/footer/footer-entry.jsx +7 -1
  36. package/client/components/layout/layout.jsx +12 -19
  37. package/client/components/main/main.jsx +15 -1
  38. package/client/components/quick-commands/qm.styl +58 -0
  39. package/client/components/quick-commands/quick-commands-box.jsx +180 -83
  40. package/client/components/quick-commands/quick-commands-select.jsx +11 -0
  41. package/client/components/session/session-control.jsx +35 -2
  42. package/client/components/session/session-control.styl +5 -0
  43. package/client/components/session/session.jsx +13 -0
  44. package/client/components/setting-panel/setting-common.jsx +2 -0
  45. package/client/components/setting-panel/tab-widgets.jsx +27 -7
  46. package/client/components/sftp/file-item.jsx +0 -13
  47. package/client/components/side-panel-r/side-panel-r.jsx +11 -4
  48. package/client/components/sidebar/bookmark-select.jsx +14 -2
  49. package/client/components/sidebar/history-item.jsx +20 -4
  50. package/client/components/sidebar/history.jsx +7 -0
  51. package/client/components/terminal/mixins/term-touch.js +18 -0
  52. package/client/components/tree-list/tree-list.jsx +31 -3
  53. package/client/components/widgets/widget-control.jsx +4 -2
  54. package/client/components/widgets/widget-instance-detail.jsx +370 -0
  55. package/client/components/widgets/widget-instance.jsx +30 -13
  56. package/client/components/widgets/widget-instances.jsx +3 -1
  57. package/client/components/widgets/widgets-list.jsx +8 -0
  58. package/client/components/widgets/widgets.styl +100 -0
  59. package/client/store/common.js +200 -54
  60. package/client/store/init-state.js +15 -0
  61. package/client/store/load-data.js +73 -0
  62. package/client/store/store.js +6 -0
  63. package/client/store/watch.js +3 -1
  64. package/client/store/widgets.js +23 -5
  65. package/package.json +1 -1
  66. 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
- <FormItem
13
- {...formItemLayout}
14
- label={e('useSshAgent')}
15
- >
16
- <Space align='center'>
17
- <FormItem
18
- name='useSshAgent'
19
- valuePropName='checked'
20
- noStyle
21
- >
22
- <SwitchLabel />
23
- </FormItem>
24
- <FormItem
25
- name='sshAgent'
26
- noStyle
27
- >
28
- <Input placeholder={e('SSH Agent Path')} />
29
- </FormItem>
30
- <HelpIcon link='https://github.com/electerm/electerm/wiki/ssh-agent' />
31
- </Space>
32
- </FormItem>
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 from the file and
6
- * reports anything it could not honour instead of dropping it silently.
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) },
@@ -22,6 +22,7 @@ const sshConfig = {
22
22
  connectionHoppings: [],
23
23
  useSshAgent: true,
24
24
  sshAgent: '',
25
+ agentForward: false,
25
26
  serverHostKey: [],
26
27
  cipher: [],
27
28
  compress: [],
@@ -6,6 +6,7 @@ const defaultValues = {
6
6
  enableSsh: true,
7
7
  enableSftp: true,
8
8
  useSshAgent: true,
9
+ agentForward: false,
9
10
  x11: false,
10
11
  term: 'xterm-256color',
11
12
  displayRaw: false,
@@ -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.zip
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, newName } = this.handleRename(toPath, typeTo === typeMap.remote)
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, zip } = this.props.transfer
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 {