@electerm/electerm-react 1.39.18 → 1.39.35

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 (37) hide show
  1. package/client/common/constants.js +7 -3
  2. package/client/common/create-title.jsx +12 -2
  3. package/client/common/default-setting.js +7 -1
  4. package/client/common/init-setting-item.js +6 -0
  5. package/client/components/bookmark-form/index.jsx +6 -2
  6. package/client/components/bookmark-form/rdp-form-ui.jsx +1 -1
  7. package/client/components/bookmark-form/rdp-form.jsx +1 -1
  8. package/client/components/bookmark-form/render-auth-ssh.jsx +27 -1
  9. package/client/components/bookmark-form/vnc-form-ui.jsx +179 -0
  10. package/client/components/bookmark-form/vnc-form.jsx +16 -0
  11. package/client/components/footer/footer-entry.jsx +1 -0
  12. package/client/components/main/term-fullscreen.styl +4 -1
  13. package/client/components/profile/profile-form-elem.jsx +87 -0
  14. package/client/components/profile/profile-form.jsx +33 -0
  15. package/client/components/profile/profile-list.jsx +79 -0
  16. package/client/components/profile/profile-transport-mod.jsx +5 -0
  17. package/client/components/profile/profile-transport.jsx +12 -0
  18. package/client/components/quick-commands/quick-command-transport-mod.jsx +13 -14
  19. package/client/components/rdp/rdp-session.jsx +53 -36
  20. package/client/components/session/session.jsx +14 -3
  21. package/client/components/session/session.styl +7 -2
  22. package/client/components/setting-panel/setting-common.jsx +1 -0
  23. package/client/components/setting-panel/setting-modal.jsx +14 -0
  24. package/client/components/setting-panel/setting-terminal.jsx +1 -1
  25. package/client/components/setting-panel/tab-profiles.jsx +38 -0
  26. package/client/components/sftp/list-table-ui.jsx +99 -46
  27. package/client/components/sftp/sftp-entry.jsx +1 -0
  28. package/client/components/sftp/sftp.styl +4 -1
  29. package/client/components/sftp/transfer-common.js +1 -1
  30. package/client/components/terminal/index.jsx +2 -4
  31. package/client/components/vnc/vnc-form.jsx +66 -0
  32. package/client/components/vnc/vnc-session.jsx +297 -0
  33. package/client/store/common.js +21 -0
  34. package/client/store/index.js +1 -0
  35. package/client/store/init-state.js +4 -23
  36. package/client/store/load-data.js +1 -2
  37. package/package.json +1 -1
@@ -68,12 +68,14 @@ export const connectionMap = buildConst([
68
68
  'serial',
69
69
  'local',
70
70
  'web',
71
- 'rdp'
71
+ 'rdp',
72
+ 'vnc'
72
73
  ])
73
74
 
74
75
  export const authTypeMap = buildConst([
75
76
  'password',
76
- 'privateKey'
77
+ 'privateKey',
78
+ 'profiles'
77
79
  ])
78
80
 
79
81
  export const footerHeight = 36
@@ -101,7 +103,8 @@ export const settingMap = buildConst([
101
103
  'terminalThemes',
102
104
  'bookmarkGroups',
103
105
  'quickCommands',
104
- 'addressBookmarks'
106
+ 'addressBookmarks',
107
+ 'profiles'
105
108
  ])
106
109
 
107
110
  export const infoTabs = buildConst([
@@ -135,6 +138,7 @@ export const terminalSplitDirectionMap = buildConst([
135
138
  export const terminalSshConfigType = 'ssh-config'
136
139
  export const terminalWebType = 'web'
137
140
  export const terminalRdpType = 'rdp'
141
+ export const terminalVncType = 'vnc'
138
142
  export const terminalSerialType = 'serial'
139
143
  export const terminalTelnetType = 'telnet'
140
144
  export const terminalLocalType = 'local'
@@ -9,7 +9,16 @@ import {
9
9
  const { prefix } = window
10
10
  const p = prefix('sftp')
11
11
 
12
- export default function createTitle (res) {
12
+ function maskHost (hostOrIp = '') {
13
+ if (/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostOrIp)) {
14
+ const arr = hostOrIp.split('.')
15
+ return arr.slice(0, arr.length - 2).join('.') + '.*.*'
16
+ } else {
17
+ return hostOrIp.replace(/^.{3}/, '***')
18
+ }
19
+ }
20
+
21
+ export default function createTitle (res, hide = true) {
13
22
  if (!res) {
14
23
  return ''
15
24
  }
@@ -17,7 +26,8 @@ export default function createTitle (res) {
17
26
  host, port, username, title, type,
18
27
  path, connectionHoppings, sshTunnels
19
28
  } = res
20
- const fixTitle = `${username || ''}@${host}:${port}`
29
+ const h = hide && window.store.config.hideIP ? maskHost(host) : host
30
+ const fixTitle = `${username || ''}@${h}:${port}`
21
31
  const extra = host || path ? (path || fixTitle) : ''
22
32
  let f = title
23
33
  ? `${title}` + (extra ? ` - ${extra}` : '')
@@ -52,5 +52,11 @@ export default {
52
52
  'activities',
53
53
  'network',
54
54
  'disks'
55
- ]
55
+ ],
56
+ filePropsEnabled: [
57
+ 'name',
58
+ 'size',
59
+ 'modifyTime'
60
+ ],
61
+ hideIP: false
56
62
  }
@@ -13,6 +13,7 @@ const { prefix } = window
13
13
  const e = prefix('control')
14
14
  const newQuickCommand = 'newQuickCommand'
15
15
  const q = prefix('quickCommands')
16
+ const f = prefix('form')
16
17
 
17
18
  export default (arr, tab) => {
18
19
  if (tab === settingMap.history) {
@@ -28,5 +29,10 @@ export default (arr, tab) => {
28
29
  id: '',
29
30
  name: q(newQuickCommand)
30
31
  }
32
+ } else if (tab === settingMap.profiles) {
33
+ return {
34
+ id: '',
35
+ name: f(settingMap.profiles)
36
+ }
31
37
  }
32
38
  }
@@ -11,6 +11,7 @@ import {
11
11
  terminalSerialType,
12
12
  terminalWebType,
13
13
  terminalRdpType,
14
+ terminalVncType,
14
15
  terminalLocalType,
15
16
  terminalTelnetType,
16
17
  newBookmarkIdPrefix
@@ -21,6 +22,7 @@ import LocalForm from './local-form'
21
22
  import TelnetForm from './telnet-form'
22
23
  import WebForm from './web-form'
23
24
  import RdpForm from './rdp-form'
25
+ import VncForm from './vnc-form'
24
26
  import { createTitleWithTag } from '../../common/create-title'
25
27
  import {
26
28
  LoadingOutlined,
@@ -43,7 +45,8 @@ export default class BookmarkIndex extends Component {
43
45
  terminalWebType,
44
46
  terminalLocalType,
45
47
  terminalSerialType,
46
- terminalRdpType
48
+ terminalRdpType,
49
+ terminalVncType
47
50
  ].includes(initType)
48
51
  ) {
49
52
  initType = connectionMap.ssh
@@ -72,7 +75,8 @@ export default class BookmarkIndex extends Component {
72
75
  [connectionMap.serial]: SerialForm,
73
76
  [connectionMap.local]: LocalForm,
74
77
  [connectionMap.web]: WebForm,
75
- [connectionMap.rdp]: RdpForm
78
+ [connectionMap.rdp]: RdpForm,
79
+ [connectionMap.vnc]: VncForm
76
80
  }
77
81
 
78
82
  handleChange = (e) => {
@@ -29,7 +29,7 @@ const { prefix } = window
29
29
  const e = prefix('form')
30
30
  const c = prefix('common')
31
31
 
32
- export default function LocalFormUi (props) {
32
+ export default function RdpFormUi (props) {
33
33
  const [
34
34
  form,
35
35
  handleFinish,
@@ -4,7 +4,7 @@
4
4
  import BookmarkForm from './ssh-form'
5
5
  import RdpFormUi from './rdp-form-ui'
6
6
 
7
- export default class WebForm extends BookmarkForm {
7
+ export default class RdpForm extends BookmarkForm {
8
8
  render () {
9
9
  return (
10
10
  <RdpFormUi
@@ -6,7 +6,8 @@ import {
6
6
  Input,
7
7
  Upload,
8
8
  AutoComplete,
9
- Form
9
+ Form,
10
+ Select
10
11
  } from 'antd'
11
12
  import { formItemLayout } from '../../common/form-layout'
12
13
  import { uniqBy } from 'lodash-es'
@@ -57,6 +58,31 @@ export default function renderAuth (props) {
57
58
  </FormItem>
58
59
  )
59
60
  }
61
+ if (authType === 'profiles') {
62
+ const opts = {
63
+ options: store.profiles
64
+ .map(d => {
65
+ return {
66
+ label: d.name,
67
+ value: d.id
68
+ }
69
+ }),
70
+ placeholder: e('profiles'),
71
+ allowClear: false
72
+ }
73
+ return (
74
+ <FormItem
75
+ {...formItemLayout}
76
+ label={e('profiles')}
77
+ name='profile'
78
+ hasFeedback
79
+ >
80
+ <Select
81
+ {...opts}
82
+ />
83
+ </FormItem>
84
+ )
85
+ }
60
86
  return [
61
87
  <FormItem
62
88
  {...formItemLayout}
@@ -0,0 +1,179 @@
1
+ /**
2
+ * web form
3
+ */
4
+
5
+ import { useEffect } from 'react'
6
+ import {
7
+ Input,
8
+ Form,
9
+ InputNumber,
10
+ TreeSelect,
11
+ Switch
12
+ } from 'antd'
13
+ import { formItemLayout } from '../../common/form-layout'
14
+ import {
15
+ newBookmarkIdPrefix,
16
+ terminalVncType
17
+ } from '../../common/constants'
18
+ import useSubmit from './use-submit'
19
+ import copy from 'json-deep-copy'
20
+ import { defaults } from 'lodash-es'
21
+ import { ColorPickerItem } from './color-picker-item.jsx'
22
+ import { getRandomDefaultColor } from '../../common/rand-hex-color.js'
23
+ import formatBookmarkGroups from './bookmark-group-tree-format'
24
+ import findBookmarkGroupId from '../../common/find-bookmark-group-id'
25
+
26
+ const FormItem = Form.Item
27
+ const { prefix } = window
28
+ const e = prefix('form')
29
+ const c = prefix('common')
30
+
31
+ export default function VncFormUi (props) {
32
+ const [
33
+ form,
34
+ handleFinish,
35
+ submitUi
36
+ ] = useSubmit(props)
37
+ useEffect(() => {
38
+ if (props.formData.id.startsWith(newBookmarkIdPrefix)) {
39
+ form.setFieldsValue({
40
+ category: props.currentBookmarkGroupId
41
+ })
42
+ }
43
+ }, [props.currentBookmarkGroupId])
44
+ const {
45
+ id = ''
46
+ } = props.formData
47
+ const {
48
+ bookmarkGroups = [],
49
+ currentBookmarkGroupId
50
+ } = props
51
+ let initialValues = copy(props.formData)
52
+ const initBookmarkGroupId = !id.startsWith(newBookmarkIdPrefix)
53
+ ? findBookmarkGroupId(bookmarkGroups, id)
54
+ : currentBookmarkGroupId
55
+ const defaultValues = {
56
+ type: terminalVncType,
57
+ port: 5900,
58
+ category: initBookmarkGroupId,
59
+ color: getRandomDefaultColor(),
60
+ viewOnly: false,
61
+ scaleViewport: true
62
+ }
63
+ initialValues = defaults(initialValues, defaultValues)
64
+ function renderCommon () {
65
+ const {
66
+ bookmarkGroups = []
67
+ } = props
68
+ const tree = formatBookmarkGroups(bookmarkGroups)
69
+ return (
70
+ <div className='pd1x'>
71
+ <FormItem
72
+ {...formItemLayout}
73
+ label={e('title')}
74
+ hasFeedback
75
+ >
76
+ <FormItem noStyle name='title'>
77
+ <Input addonBefore={<ColorPickerItem />} />
78
+ </FormItem>
79
+ </FormItem>
80
+ <FormItem
81
+ {...formItemLayout}
82
+ label={e('host')}
83
+ hasFeedback
84
+ name='host'
85
+ required
86
+ >
87
+ <Input />
88
+ </FormItem>
89
+ <FormItem
90
+ {...formItemLayout}
91
+ label={e('port')}
92
+ hasFeedback
93
+ name='port'
94
+ rules={[{
95
+ required: true, message: 'port required'
96
+ }]}
97
+ >
98
+ <InputNumber
99
+ placeholder={e('port')}
100
+ min={1}
101
+ max={65535}
102
+ step={1}
103
+ />
104
+ </FormItem>
105
+ <FormItem
106
+ {...formItemLayout}
107
+ label={e('viewOnly')}
108
+ name='viewOnly'
109
+ valuePropName='checked'
110
+ >
111
+ <Switch />
112
+ </FormItem>
113
+ <FormItem
114
+ {...formItemLayout}
115
+ label={e('scaleViewport')}
116
+ name='scaleViewport'
117
+ valuePropName='checked'
118
+ >
119
+ <Switch />
120
+ </FormItem>
121
+ <FormItem
122
+ {...formItemLayout}
123
+ label={e('username')}
124
+ hasFeedback
125
+ name='username'
126
+ >
127
+ <Input />
128
+ </FormItem>
129
+ <FormItem
130
+ {...formItemLayout}
131
+ label={e('password')}
132
+ hasFeedback
133
+ name='password'
134
+ >
135
+ <Input.Password />
136
+ </FormItem>
137
+ <FormItem
138
+ {...formItemLayout}
139
+ label={e('description')}
140
+ name='description'
141
+ hasFeedback
142
+ >
143
+ <Input.TextArea rows={1} />
144
+ </FormItem>
145
+ <FormItem
146
+ {...formItemLayout}
147
+ label={c('bookmarkCategory')}
148
+ name='category'
149
+ >
150
+ <TreeSelect
151
+ treeData={tree}
152
+ treeDefaultExpandAll
153
+ showSearch
154
+ />
155
+ </FormItem>
156
+ <FormItem
157
+ {...formItemLayout}
158
+ label='type'
159
+ name='type'
160
+ className='hide'
161
+ >
162
+ <Input />
163
+ </FormItem>
164
+ </div>
165
+ )
166
+ }
167
+
168
+ return (
169
+ <Form
170
+ form={form}
171
+ onFinish={handleFinish}
172
+ initialValues={initialValues}
173
+ name='vnc-form'
174
+ >
175
+ {renderCommon()}
176
+ {submitUi}
177
+ </Form>
178
+ )
179
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * bookmark form
3
+ */
4
+ import BookmarkForm from './ssh-form'
5
+ import VncFormUi from './vnc-form-ui'
6
+
7
+ export default class VncForm extends BookmarkForm {
8
+ render () {
9
+ return (
10
+ <VncFormUi
11
+ {...this.props}
12
+ {...this.getProps()}
13
+ />
14
+ )
15
+ }
16
+ }
@@ -129,6 +129,7 @@ export default class SystemMenu extends Component {
129
129
  if (
130
130
  type === 'rdp' ||
131
131
  type === 'web' ||
132
+ type === 'vnc' ||
132
133
  pane === paneMap.fileManager ||
133
134
  !tabs.length
134
135
  ) {
@@ -5,6 +5,7 @@
5
5
  .terminal-control
6
6
  .qm-wrap-tooltip
7
7
  .main-footer
8
+ .session-v-info
8
9
  display none
9
10
  .term-fullscreen-control
10
11
  display block
@@ -12,7 +13,9 @@
12
13
  top 10px
13
14
  position fixed
14
15
  z-index 100
16
+ background rgba(45, 245, 108, 0.8)
15
17
  .term-wrap
18
+ .session-v-wrap
16
19
  position fixed
17
20
  left 0 !important
18
21
  top 0 !important
@@ -24,4 +27,4 @@
24
27
  right 10px !important
25
28
  bottom 10px !important
26
29
  .term-fullscreen-control
27
- display none
30
+ display none
@@ -0,0 +1,87 @@
1
+ import {
2
+ Form,
3
+ message,
4
+ Button
5
+ } from 'antd'
6
+ import generate from '../../common/uid'
7
+ import InputAutoFocus from '../common/input-auto-focus'
8
+ import renderAuth from '../bookmark-form/render-auth-ssh'
9
+ import { formItemLayout } from '../../common/form-layout'
10
+ import {
11
+ settingMap
12
+ } from '../../common/constants'
13
+ const FormItem = Form.Item
14
+ const { prefix } = window
15
+ const e = prefix('form')
16
+ const s = prefix('setting')
17
+ const ss = prefix('sftp')
18
+
19
+ export default function QuickCommandForm (props) {
20
+ const [form] = Form.useForm()
21
+ const { autofocustrigger } = props.store
22
+ async function handleSubmit (res) {
23
+ const { formData } = props
24
+ console.log(res)
25
+ const update1 = {
26
+ ...res,
27
+ id: generate()
28
+ }
29
+ if (formData.id) {
30
+ props.store.editItem(formData.id, res, settingMap.profiles)
31
+ } else {
32
+ props.store.addItem(update1, settingMap.profiles)
33
+ props.store.setSettingItem({
34
+ id: '',
35
+ name: e('profile')
36
+ })
37
+ }
38
+ message.success(s('saved'))
39
+ }
40
+ return (
41
+ <Form
42
+ form={form}
43
+ onFinish={handleSubmit}
44
+ className='form-wrap pd2l'
45
+ layout='vertical'
46
+ initialValues={props.formData}
47
+ >
48
+ <FormItem
49
+ label={e('profileName')}
50
+ {...formItemLayout}
51
+ rules={[{
52
+ max: 60, message: '60 chars max'
53
+ }, {
54
+ required: true, message: 'Name required'
55
+ }]}
56
+ hasFeedback
57
+ name='name'
58
+ >
59
+ <InputAutoFocus
60
+ selectall='yes'
61
+ autofocustrigger={autofocustrigger}
62
+ />
63
+ </FormItem>
64
+ {
65
+ renderAuth({
66
+ store: props.store,
67
+ form,
68
+ authType: 'password'
69
+ })
70
+ }
71
+ {
72
+ renderAuth({
73
+ store: props.store,
74
+ form
75
+ })
76
+ }
77
+ <FormItem>
78
+ <Button
79
+ type='primary'
80
+ htmlType='submit'
81
+ >
82
+ {ss('submit')}
83
+ </Button>
84
+ </FormItem>
85
+ </Form>
86
+ )
87
+ }
@@ -0,0 +1,33 @@
1
+ import { PureComponent } from 'react'
2
+ import ProfileCommandForm from './profile-form-elem'
3
+ import { LoadingOutlined } from '@ant-design/icons'
4
+
5
+ export default class ProfleFormIndex extends PureComponent {
6
+ state = {
7
+ ready: false
8
+ }
9
+
10
+ componentDidMount () {
11
+ this.timer = setTimeout(() => {
12
+ this.setState({
13
+ ready: true
14
+ })
15
+ }, 200)
16
+ }
17
+
18
+ componentWillUnmount () {
19
+ clearTimeout(this.timer)
20
+ }
21
+
22
+ render () {
23
+ const { ready } = this.state
24
+ if (!ready) {
25
+ return (
26
+ <div className='pd3 aligncenter'>
27
+ <LoadingOutlined />
28
+ </div>
29
+ )
30
+ }
31
+ return <ProfileCommandForm {...this.props} />
32
+ }
33
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * quick command list render
3
+ */
4
+
5
+ import List from '../setting-panel/list'
6
+ import { PlusOutlined } from '@ant-design/icons'
7
+ import classnames from 'classnames'
8
+ import highlight from '../common/highlight'
9
+ import ProfileTransport from './profile-transport'
10
+ import {
11
+ settingMap
12
+ } from '../../common/constants'
13
+
14
+ export default class ProfileList extends List {
15
+ del = (item, e) => {
16
+ e.stopPropagation()
17
+ this.props.store.delItem(item, settingMap.profiles)
18
+ }
19
+
20
+ onClickItem = (item) => {
21
+ this.props.onClickItem(item)
22
+ }
23
+
24
+ renderItem = (item, i) => {
25
+ if (!item) {
26
+ return null
27
+ }
28
+ const { activeItemId } = this.props
29
+ const { name, id } = item
30
+ const cls = classnames(
31
+ 'item-list-unit theme-item',
32
+ {
33
+ active: activeItemId === id
34
+ }
35
+ )
36
+ let title = name
37
+ title = highlight(
38
+ title,
39
+ this.state.keyword
40
+ )
41
+ return (
42
+ <div
43
+ key={i + id}
44
+ className={cls}
45
+ onClick={() => this.onClickItem(item)}
46
+ data-id={id}
47
+ >
48
+ <div className='elli pd1y pd2x' title={name}>
49
+ {
50
+ !id
51
+ ? <PlusOutlined className='mg1r' />
52
+ : null
53
+ }
54
+ {title}
55
+ </div>
56
+ {this.renderDelBtn(item)}
57
+ </div>
58
+ )
59
+ }
60
+
61
+ renderTransport = () => {
62
+ return (
63
+ <ProfileTransport
64
+ store={this.props.store}
65
+ />
66
+ )
67
+ }
68
+
69
+ filter = list => {
70
+ const { keyword } = this.state
71
+ return keyword
72
+ ? list.filter((item) => {
73
+ const n = (item.name || '').toLowerCase()
74
+ const k = keyword.toLowerCase()
75
+ return n.includes(k)
76
+ })
77
+ : list
78
+ }
79
+ }
@@ -0,0 +1,5 @@
1
+ import QmTransport from '../quick-commands/quick-command-transport'
2
+
3
+ export default class ProfileTransport extends QmTransport {
4
+ name = 'profiles'
5
+ }
@@ -0,0 +1,12 @@
1
+ import ProfileTransportMod from './profile-transport-mod'
2
+ import { Component } from '../common/react-subx'
3
+
4
+ export default class ProfileTransport extends Component {
5
+ render () {
6
+ return (
7
+ <div className='pd1b'>
8
+ <ProfileTransportMod store={this.props.store} />
9
+ </div>
10
+ )
11
+ }
12
+ }