@electerm/electerm-react 2.3.126 → 2.3.151

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 (60) hide show
  1. package/client/common/constants.js +2 -1
  2. package/client/common/db.js +2 -1
  3. package/client/common/download.jsx +15 -0
  4. package/client/common/init-setting-item.js +5 -0
  5. package/client/components/ai/ai-cache.jsx +1 -1
  6. package/client/components/batch-op/batch-op.jsx +4 -9
  7. package/client/components/bookmark-form/common/bookmark-group-tree-format.js +4 -3
  8. package/client/components/bookmark-form/common/color-picker.jsx +16 -5
  9. package/client/components/bookmark-form/common/color-picker.styl +1 -2
  10. package/client/components/bookmark-form/common/connection-hopping.jsx +1 -0
  11. package/client/components/bookmark-form/common/ssh-host-selector.jsx +1 -1
  12. package/client/components/bookmark-form/common/ssh-tunnels.jsx +1 -0
  13. package/client/components/bookmark-form/config/common-fields.js +1 -0
  14. package/client/components/bookmark-form/tree-delete.jsx +18 -15
  15. package/client/components/common/drawer.jsx +62 -0
  16. package/client/components/common/drawer.styl +34 -0
  17. package/client/components/common/input-auto-focus.jsx +16 -3
  18. package/client/components/common/logo-elem.jsx +1 -1
  19. package/client/components/common/password.jsx +1 -1
  20. package/client/components/footer/footer.styl +1 -0
  21. package/client/components/main/main.jsx +4 -1
  22. package/client/components/profile/profile-form.jsx +1 -1
  23. package/client/components/quick-commands/qm.styl +21 -1
  24. package/client/components/quick-commands/quick-commands-box.jsx +28 -36
  25. package/client/components/quick-commands/quick-commands-form.jsx +1 -1
  26. package/client/components/quick-commands/quick-commands-list-form.jsx +1 -0
  27. package/client/components/quick-commands/quick-commands-select.jsx +1 -1
  28. package/client/components/session/session.jsx +1 -1
  29. package/client/components/setting-panel/deep-link-control.jsx +1 -0
  30. package/client/components/setting-panel/list.jsx +1 -1
  31. package/client/components/setting-panel/list.styl +1 -1
  32. package/client/components/setting-panel/setting-common.jsx +2 -1
  33. package/client/components/setting-panel/setting-modal.jsx +13 -0
  34. package/client/components/setting-panel/setting-terminal.jsx +1 -1
  35. package/client/components/setting-panel/setting-wrap.jsx +5 -11
  36. package/client/components/setting-panel/setting-wrap.styl +8 -6
  37. package/client/components/setting-panel/start-session-select.jsx +3 -3
  38. package/client/components/setting-panel/tab-widgets.jsx +35 -0
  39. package/client/components/sftp/address-bookmark-item.jsx +1 -1
  40. package/client/components/sftp/paged-list.jsx +2 -1
  41. package/client/components/sftp/permission-render.jsx +1 -1
  42. package/client/components/sftp/sftp-entry.jsx +1 -1
  43. package/client/components/sftp/sftp.styl +13 -0
  44. package/client/components/sidebar/index.jsx +12 -2
  45. package/client/components/sidebar/transfer-history-modal.jsx +1 -1
  46. package/client/components/theme/theme-list-item.jsx +4 -3
  47. package/client/components/tree-list/move-item-modal.jsx +171 -36
  48. package/client/components/tree-list/tree-list.jsx +3 -15
  49. package/client/components/tree-list/tree-list.styl +6 -1
  50. package/client/components/vnc/vnc-session.jsx +1 -1
  51. package/client/components/widgets/widget-control.jsx +64 -0
  52. package/client/components/widgets/widget-form.jsx +115 -0
  53. package/client/components/widgets/widget-instance.jsx +46 -0
  54. package/client/components/widgets/widget-instances.jsx +10 -0
  55. package/client/components/widgets/widgets-list.jsx +155 -0
  56. package/client/store/init-state.js +9 -1
  57. package/client/store/setting.js +1 -3
  58. package/client/store/store.js +2 -0
  59. package/client/store/widgets.js +59 -0
  60. package/package.json +1 -1
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Widget control component - shows form for a selected widget
3
+ */
4
+ import React, { useState } from 'react'
5
+ import WidgetForm from './widget-form'
6
+ import {
7
+ message
8
+ } from 'antd'
9
+
10
+ export default function WidgetControl ({ formData }) {
11
+ const [loading, setLoading] = useState(false)
12
+ const widget = formData
13
+ if (!widget.id) {
14
+ return (
15
+ <div className='widget-control-empty aligncenter pd3'>
16
+ <p>Select a widget to configure</p>
17
+ </div>
18
+ )
19
+ }
20
+
21
+ const handleFormSubmit = async (config) => {
22
+ setLoading(true)
23
+ try {
24
+ const result = await window.store.runWidget(widget.id, config)
25
+ const {
26
+ instanceId,
27
+ success,
28
+ error,
29
+ msg
30
+ } = result
31
+ if (!instanceId) {
32
+ if (success === false) {
33
+ message.error('Failed to run widget', error || '')
34
+ } else {
35
+ message.success(msg || 'Widget run successfully')
36
+ }
37
+ return
38
+ }
39
+ // Add instance to the store
40
+ const instance = {
41
+ id: result.instanceId,
42
+ title: `${widget.info.name} (${result.instanceId})`,
43
+ widgetId: result.widgetId,
44
+ serverInfo: result.serverInfo,
45
+ config
46
+ }
47
+ window.store.widgetInstances.push(instance)
48
+ } catch (err) {
49
+ console.error('Failed to run widget:', err)
50
+ } finally {
51
+ setLoading(false)
52
+ }
53
+ }
54
+
55
+ return (
56
+ <div className='widget-control'>
57
+ <WidgetForm
58
+ widget={widget}
59
+ onSubmit={handleFormSubmit}
60
+ loading={loading}
61
+ />
62
+ </div>
63
+ )
64
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Widget form component
3
+ */
4
+ import React from 'react'
5
+ import { Form, Input, InputNumber, Switch, Select, Button, message } from 'antd'
6
+ import { formItemLayout, tailFormItemLayout } from '../../common/form-layout'
7
+
8
+ export default function WidgetForm ({ widget, onSubmit, loading }) {
9
+ const [form] = Form.useForm()
10
+
11
+ if (!widget) {
12
+ return null
13
+ }
14
+
15
+ const { info } = widget
16
+ const { configs, type } = info
17
+ const isInstanceWidget = type === 'instance'
18
+ const txt = isInstanceWidget ? 'Start widget' : 'Run widget'
19
+
20
+ const handleSubmit = async (values) => {
21
+ try {
22
+ await onSubmit(values)
23
+ message.success('Widget started successfully')
24
+ } catch (error) {
25
+ message.error('Failed to start widget: ' + error.message)
26
+ }
27
+ }
28
+
29
+ const renderFormItem = (config) => {
30
+ const { name, type, description, choices } = config
31
+ let control = null
32
+
33
+ switch (type) {
34
+ case 'string':
35
+ control = <Input placeholder={description} />
36
+ break
37
+ case 'number':
38
+ control = <InputNumber style={{ width: '100%' }} placeholder={description} />
39
+ break
40
+ case 'boolean':
41
+ return (
42
+ <Form.Item
43
+ key={name}
44
+ {...formItemLayout}
45
+ label={name}
46
+ name={name}
47
+ valuePropName='checked'
48
+ tooltip={description}
49
+ >
50
+ <Switch />
51
+ </Form.Item>
52
+ )
53
+ default:
54
+ control = <Input placeholder={description} />
55
+ }
56
+
57
+ if (choices && choices.length > 0) {
58
+ control = (
59
+ <Select placeholder={description}>
60
+ {choices.map(choice => (
61
+ <Select.Option key={choice} value={choice}>
62
+ {choice}
63
+ </Select.Option>
64
+ ))}
65
+ </Select>
66
+ )
67
+ }
68
+
69
+ return (
70
+ <Form.Item
71
+ key={name}
72
+ {...formItemLayout}
73
+ label={name}
74
+ name={name}
75
+ tooltip={description}
76
+ >
77
+ {control}
78
+ </Form.Item>
79
+ )
80
+ }
81
+
82
+ const initialValues = configs.reduce((acc, config) => {
83
+ acc[config.name] = config.default
84
+ return acc
85
+ }, {})
86
+
87
+ return (
88
+ <div className='widget-form'>
89
+ <div className='pd1b'>
90
+ <h4>{info.name}</h4>
91
+ <p>{info.description}</p>
92
+ </div>
93
+ <Form
94
+ form={form}
95
+ onFinish={handleSubmit}
96
+ initialValues={initialValues}
97
+ layout='horizontal'
98
+ >
99
+ {configs.map(renderFormItem)}
100
+ <Form.Item
101
+ {...tailFormItemLayout}
102
+ >
103
+ <Button
104
+ type='primary'
105
+ htmlType='submit'
106
+ loading={loading}
107
+ disabled={loading}
108
+ >
109
+ {txt}
110
+ </Button>
111
+ </Form.Item>
112
+ </Form>
113
+ </div>
114
+ )
115
+ }
@@ -0,0 +1,46 @@
1
+ import { Popconfirm } from 'antd'
2
+ import { CloseOutlined } from '@ant-design/icons'
3
+
4
+ const e = window.translate
5
+
6
+ export default function WidgetInstance ({ item }) {
7
+ const { id, title } = item
8
+ const cls = 'item-list-unit'
9
+ const delProps = {
10
+ title: e('del'),
11
+ className: 'pointer list-item-remove'
12
+ }
13
+ const icon = (
14
+ <CloseOutlined
15
+ {...delProps}
16
+ />
17
+ )
18
+ function onConfirm () {
19
+ window.store.stopWidget(id)
20
+ }
21
+ const popProps = {
22
+ title: e('del') + '?',
23
+ onConfirm,
24
+ okText: e('del'),
25
+ cancelText: e('cancel'),
26
+ placement: 'top'
27
+ }
28
+ return (
29
+ <div
30
+ key={id}
31
+ className={cls}
32
+ >
33
+ <div
34
+ title={title}
35
+ className='elli pd1y pd2x list-item-title'
36
+ >
37
+ {title}
38
+ </div>
39
+ <Popconfirm
40
+ {...popProps}
41
+ >
42
+ {icon}
43
+ </Popconfirm>
44
+ </div>
45
+ )
46
+ }
@@ -0,0 +1,10 @@
1
+ import WidgetInstance from './widget-instance'
2
+
3
+ export default function WidgetInstances ({ widgetInstances }) {
4
+ return widgetInstances.map(item => (
5
+ <WidgetInstance
6
+ key={item.id}
7
+ item={item}
8
+ />
9
+ ))
10
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * widgets list
3
+ */
4
+ import React, { useState, useEffect } from 'react'
5
+ import {
6
+ Input,
7
+ Tabs
8
+ } from 'antd'
9
+ import WidgetInstances from './widget-instances'
10
+ import classnames from 'classnames'
11
+ import highlight from '../common/highlight'
12
+ import {
13
+ auto
14
+ } from 'manate/react'
15
+
16
+ const e = window.translate
17
+
18
+ export default auto(function WidgetsList ({ activeItemId, store }) {
19
+ const { widgetInstances } = store
20
+ const [tab, setTab] = useState('widgets') // or instances
21
+ const [widgets, setWidgets] = useState([])
22
+ const [keyword, setKeyword] = useState('')
23
+ const [ready, setReady] = useState(false)
24
+
25
+ useEffect(() => {
26
+ const timer = setTimeout(() => {
27
+ setReady(true)
28
+ }, 200)
29
+ loadWidgets()
30
+ return () => {
31
+ clearTimeout(timer)
32
+ }
33
+ }, [])
34
+
35
+ const loadWidgets = async () => {
36
+ try {
37
+ const widgets = await window.store.listWidgets()
38
+ setWidgets(widgets)
39
+ } catch (error) {
40
+ console.error('Failed to load widgets:', error)
41
+ }
42
+ }
43
+
44
+ const handleSearch = (e) => {
45
+ setKeyword(e.target.value)
46
+ }
47
+
48
+ const handleTabChange = (key) => {
49
+ setTab(key)
50
+ }
51
+
52
+ const onClickWidget = (widget) => {
53
+ window.store.setSettingItem(widget)
54
+ }
55
+
56
+ const renderWidgetItem = (widget, i) => {
57
+ const title = widget.info.name
58
+ const tag = ''
59
+ const cls = classnames(
60
+ 'item-list-unit',
61
+ {
62
+ active: activeItemId === widget.id
63
+ }
64
+ )
65
+ const titleHighlight = highlight(
66
+ title,
67
+ keyword
68
+ )
69
+ return (
70
+ <div
71
+ key={widget.id}
72
+ className={cls}
73
+ onClick={() => onClickWidget(widget)}
74
+ >
75
+ <div
76
+ title={title}
77
+ className='elli pd1y pd2x list-item-title'
78
+ >
79
+ {tag}{titleHighlight || e('new')}
80
+ </div>
81
+ </div>
82
+ )
83
+ }
84
+
85
+ const renderWidgetsList = () => {
86
+ const filteredWidgets = keyword
87
+ ? widgets.filter(widget => widget.info.name.toLowerCase().includes(keyword.toLowerCase()))
88
+ : widgets
89
+
90
+ return (
91
+ <div className='item-list item-type-widgets'>
92
+ <div className='pd1y'>
93
+ <Input.Search
94
+ type='text'
95
+ placeholder='Search widgets...'
96
+ value={keyword}
97
+ onChange={handleSearch}
98
+ className='form-control'
99
+ />
100
+ </div>
101
+ <div className='item-list-wrap pd1y'>
102
+ {filteredWidgets.map(renderWidgetItem)}
103
+ </div>
104
+ </div>
105
+ )
106
+ }
107
+
108
+ const renderTabs = () => {
109
+ const instancesTag = e('runningInstances') + ` (${widgetInstances.length})`
110
+ const items = [
111
+ {
112
+ key: 'widgets',
113
+ label: e('widgets'),
114
+ children: null
115
+ },
116
+ {
117
+ key: 'instances',
118
+ label: instancesTag,
119
+ children: null
120
+ }
121
+ ]
122
+ return (
123
+ <Tabs
124
+ activeKey={tab}
125
+ onChange={handleTabChange}
126
+ items={items}
127
+ />
128
+ )
129
+ }
130
+
131
+ const renderInstancesSection = () => {
132
+ return (
133
+ <WidgetInstances
134
+ widgetInstances={widgetInstances}
135
+ />
136
+ )
137
+ }
138
+
139
+ if (!ready) {
140
+ return null
141
+ }
142
+
143
+ return (
144
+ <div>
145
+ {renderTabs()}
146
+ <div className='pd2x pd1y'>
147
+ {
148
+ tab === 'widgets'
149
+ ? renderWidgetsList()
150
+ : renderInstancesSection()
151
+ }
152
+ </div>
153
+ </div>
154
+ )
155
+ })
@@ -190,6 +190,14 @@ export default () => {
190
190
  hasNodePty: window.pre.runSync('nodePtyCheck'),
191
191
  terminalFullScreen: false,
192
192
  hideDelKeyTip: ls.getItem(dismissDelKeyTipLsKey) === 'y',
193
- tabsHeight: 36
193
+ tabsHeight: 36,
194
+
195
+ // widgets
196
+ widgets: [],
197
+ widgetInstances: [],
198
+ // move item
199
+ openMoveModal: false,
200
+ moveItem: null,
201
+ moveItemIsGroup: false
194
202
  }
195
203
  }
@@ -91,9 +91,7 @@ export default Store => {
91
91
  ) {
92
92
  return store.hideSettingModal()
93
93
  }
94
- store.storeAssign({
95
- settingTab: settingMap.setting
96
- })
94
+ store.settingTab = settingMap.setting
97
95
  store.setSettingItem(getInitItem([], settingMap.setting))
98
96
  store.openSettingModal()
99
97
  }
@@ -24,6 +24,7 @@ import transferHistoryExtend from './transfer-history'
24
24
  import batchInputHistory from './batch-input-history'
25
25
  import transferExtend from './transfer-list'
26
26
  import addressBookmarkExtend from './address-bookmark'
27
+ import widgetsExtend from './widgets'
27
28
  import isColorDark from '../common/is-color-dark'
28
29
  import { getReverseColor } from '../common/reverse-color'
29
30
  import { uniq } from 'lodash-es'
@@ -295,5 +296,6 @@ transferHistoryExtend(Store)
295
296
  batchInputHistory(Store)
296
297
  transferExtend(Store)
297
298
  addressBookmarkExtend(Store)
299
+ widgetsExtend(Store)
298
300
 
299
301
  export const StateStore = Store
@@ -0,0 +1,59 @@
1
+ /**
2
+ * widgets related functions
3
+ */
4
+
5
+ import {
6
+ message
7
+ } from 'antd'
8
+ import {
9
+ settingMap
10
+ } from '../common/constants'
11
+ import getInitItem from '../common/init-setting-item'
12
+
13
+ export default Store => {
14
+ Store.prototype.listWidgets = async () => {
15
+ return window.pre.runGlobalAsync('listWidgets')
16
+ }
17
+
18
+ Store.prototype.runWidget = async (widgetId, config) => {
19
+ return window.pre.runGlobalAsync('runWidget', widgetId, config)
20
+ }
21
+
22
+ Store.prototype.deleteWidgetInstance = (instanceId) => {
23
+ const {
24
+ widgetInstances
25
+ } = window.store
26
+ const index = widgetInstances.findIndex(w => w.id === instanceId)
27
+ if (index > -1) {
28
+ widgetInstances.splice(index, 1)
29
+ }
30
+ }
31
+
32
+ Store.prototype.stopWidget = async (instanceId) => {
33
+ const {
34
+ store
35
+ } = window
36
+ const r = await window.pre.runGlobalAsync('stopWidget', instanceId)
37
+ .catch(err => {
38
+ console.error('stopWidget error', err)
39
+ message.error(window.translate('stopWidgetFailed') + ': ' + err.message)
40
+ return false
41
+ })
42
+ if (r) {
43
+ store.deleteWidgetInstance(instanceId)
44
+ }
45
+ }
46
+
47
+ Store.prototype.runWidgetFunc = async (instanceId, funcName, ...args) => {
48
+ return window.pre.runGlobalAsync('runWidgetFunc', instanceId, funcName, ...args)
49
+ }
50
+
51
+ Store.prototype.openWidgetsModal = () => {
52
+ const {
53
+ store
54
+ } = window
55
+ store.setSettingItem(getInitItem([], settingMap.widgets))
56
+ store.settingTab = settingMap.widgets
57
+ store.openSettingModal()
58
+ }
59
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "2.3.126",
3
+ "version": "2.3.151",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",