@electerm/electerm-react 3.15.153 → 3.15.155
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/components/bookmark-form/bookmark-form.styl +9 -0
- package/client/components/bookmark-form/common/run-scripts.jsx +1 -1
- package/client/components/quick-commands/qm.styl +24 -1
- package/client/components/quick-commands/quick-commands-list-form.jsx +1 -1
- package/client/components/session/session.jsx +2 -1
- package/client/components/setting-panel/deep-link-control.jsx +2 -2
- package/client/components/setting-panel/setting-passwords.jsx +60 -88
- package/client/components/setting-panel/setting.styl +37 -1
- package/client/components/setting-sync/data-import.jsx +1 -1
- package/client/components/sidebar/sidebar-panel.jsx +5 -0
- package/client/css/mobile.styl +32 -0
- package/client/store/sidebar.js +9 -0
- package/package.json +1 -1
|
@@ -16,6 +16,15 @@
|
|
|
16
16
|
.number-input
|
|
17
17
|
min-width 210px
|
|
18
18
|
|
|
19
|
+
// On mobile, stack loginScriptDelay and loginScript vertically instead of
|
|
20
|
+
// horizontally (the antd Space renders them side by side by default).
|
|
21
|
+
.is-mobile
|
|
22
|
+
.run-scripts-space.ant-space
|
|
23
|
+
flex-direction column
|
|
24
|
+
align-items stretch
|
|
25
|
+
.ant-space-item
|
|
26
|
+
width 100%
|
|
27
|
+
|
|
19
28
|
.tree-select-wrapper
|
|
20
29
|
display flex
|
|
21
30
|
flex-direction column
|
|
@@ -78,4 +78,27 @@
|
|
|
78
78
|
max-width none
|
|
79
79
|
width 100%
|
|
80
80
|
.qm-label-select
|
|
81
|
-
width 100%
|
|
81
|
+
width 100%
|
|
82
|
+
|
|
83
|
+
// On mobile, stack the quick-command row (name / delay / command / remove)
|
|
84
|
+
// vertically instead of side by side — the horizontal Space.Compact layout
|
|
85
|
+
// is too cramped on narrow screens.
|
|
86
|
+
.is-mobile
|
|
87
|
+
.qm-cmd-row.ant-space-compact
|
|
88
|
+
display flex
|
|
89
|
+
flex-direction column
|
|
90
|
+
align-items stretch
|
|
91
|
+
gap 8px
|
|
92
|
+
.ant-space-compact-item
|
|
93
|
+
width 100%
|
|
94
|
+
// reset the constrained widths so inputs fill the row when stacked
|
|
95
|
+
.qm-name-input
|
|
96
|
+
max-width none
|
|
97
|
+
width 100%
|
|
98
|
+
.ant-input-number
|
|
99
|
+
width 100% !important
|
|
100
|
+
// keep the drag handle + remove button on a tidy line of their own
|
|
101
|
+
> .drag
|
|
102
|
+
align-self flex-start
|
|
103
|
+
> button:last-child
|
|
104
|
+
align-self flex-end
|
|
@@ -656,7 +656,8 @@ export default class SessionWrapper extends Component {
|
|
|
656
656
|
const simpleMapper = {
|
|
657
657
|
[paneMap.terminal]: 'T',
|
|
658
658
|
[paneMap.fileManager]: 'F',
|
|
659
|
-
[paneMap.ssh]: 'T'
|
|
659
|
+
[paneMap.ssh]: 'T',
|
|
660
|
+
[paneMap.sftp]: 'S'
|
|
660
661
|
}
|
|
661
662
|
return (
|
|
662
663
|
<div className='term-sftp-tabs fleft'>
|
|
@@ -110,7 +110,7 @@ export default function DeepLinkControl () {
|
|
|
110
110
|
<Tooltip
|
|
111
111
|
title={renderTooltipContent()}
|
|
112
112
|
>
|
|
113
|
-
<
|
|
113
|
+
<div className='deep-link-btns'>
|
|
114
114
|
{
|
|
115
115
|
!isAllRegistered && (
|
|
116
116
|
<Button
|
|
@@ -134,7 +134,7 @@ export default function DeepLinkControl () {
|
|
|
134
134
|
</Button>
|
|
135
135
|
)
|
|
136
136
|
}
|
|
137
|
-
</
|
|
137
|
+
</div>
|
|
138
138
|
</Tooltip>
|
|
139
139
|
</div>
|
|
140
140
|
)
|
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
} from '@ant-design/icons'
|
|
12
12
|
import {
|
|
13
13
|
Button,
|
|
14
|
+
Pagination,
|
|
14
15
|
Modal,
|
|
15
16
|
Space,
|
|
16
|
-
Table,
|
|
17
17
|
Tag,
|
|
18
18
|
Tooltip,
|
|
19
19
|
Typography,
|
|
@@ -126,8 +126,10 @@ export default class SettingPasswords extends Component {
|
|
|
126
126
|
})
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
|
|
130
|
-
this.setState({
|
|
129
|
+
handlePageChange = (page, pageSize) => {
|
|
130
|
+
this.setState({
|
|
131
|
+
pagination: { current: page, pageSize }
|
|
132
|
+
})
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
getFilteredData = () => {
|
|
@@ -143,88 +145,48 @@ export default class SettingPasswords extends Component {
|
|
|
143
145
|
})
|
|
144
146
|
}
|
|
145
147
|
|
|
146
|
-
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
{
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const props0 = {
|
|
184
|
-
title: titles.join('\n'),
|
|
185
|
-
children: <span>{display}</span>
|
|
186
|
-
}
|
|
187
|
-
return <Tooltip {...props0} />
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
title: e('actions'),
|
|
192
|
-
key: 'actions',
|
|
193
|
-
width: 80,
|
|
194
|
-
render: (_, record) => {
|
|
195
|
-
const copyProps0 = {
|
|
196
|
-
type: 'text',
|
|
197
|
-
icon: <CopyOutlined />,
|
|
198
|
-
onClick: () => this.handleCopyPassword(record.password)
|
|
199
|
-
}
|
|
200
|
-
const editProps0 = {
|
|
201
|
-
type: 'text',
|
|
202
|
-
icon: <EditOutlined />,
|
|
203
|
-
onClick: () => this.showEditModal(record)
|
|
204
|
-
}
|
|
205
|
-
const copyTooltipProps = {
|
|
206
|
-
title: e('copy'),
|
|
207
|
-
children: <Button {...copyProps0} />
|
|
208
|
-
}
|
|
209
|
-
const editTooltipProps = {
|
|
210
|
-
title: e('changePassword'),
|
|
211
|
-
children: <Button {...editProps0} />
|
|
212
|
-
}
|
|
213
|
-
const spaceProps0 = {
|
|
214
|
-
children: [
|
|
215
|
-
<Tooltip key='copy' {...copyTooltipProps} />,
|
|
216
|
-
<Tooltip key='edit' {...editTooltipProps} />
|
|
217
|
-
]
|
|
218
|
-
}
|
|
219
|
-
return <Space>{spaceProps0.children}</Space>
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
]
|
|
223
|
-
return columns
|
|
148
|
+
renderItem = (record) => {
|
|
149
|
+
const { password, count, titles } = record
|
|
150
|
+
const display = titles.length > 2
|
|
151
|
+
? `${titles.slice(0, 2).join(', ')}... (+${titles.length - 2})`
|
|
152
|
+
: titles.join(', ')
|
|
153
|
+
const copyProps = {
|
|
154
|
+
type: 'text',
|
|
155
|
+
icon: <CopyOutlined />,
|
|
156
|
+
onClick: () => this.handleCopyPassword(password)
|
|
157
|
+
}
|
|
158
|
+
const editProps = {
|
|
159
|
+
type: 'text',
|
|
160
|
+
icon: <EditOutlined />,
|
|
161
|
+
onClick: () => this.showEditModal(record)
|
|
162
|
+
}
|
|
163
|
+
return (
|
|
164
|
+
<div className='setting-passwords-item' key={password}>
|
|
165
|
+
<div className='setting-passwords-item-inner'>
|
|
166
|
+
<Space className='setting-passwords-pwd-cell'>
|
|
167
|
+
<KeyOutlined />
|
|
168
|
+
<TextAnt keyboard>********</TextAnt>
|
|
169
|
+
</Space>
|
|
170
|
+
<Tag color='blue' className='setting-passwords-count-cell'>{count}</Tag>
|
|
171
|
+
<Tooltip title={titles.join('\n')}>
|
|
172
|
+
<span className='setting-passwords-host-cell'>{display}</span>
|
|
173
|
+
</Tooltip>
|
|
174
|
+
<Space className='setting-passwords-actions-cell'>
|
|
175
|
+
<Tooltip title={e('copy')}>
|
|
176
|
+
<Button {...copyProps} />
|
|
177
|
+
</Tooltip>
|
|
178
|
+
<Tooltip title={e('changePassword')}>
|
|
179
|
+
<Button {...editProps} />
|
|
180
|
+
</Tooltip>
|
|
181
|
+
</Space>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
)
|
|
224
185
|
}
|
|
225
186
|
|
|
226
187
|
renderContent () {
|
|
227
188
|
const { search, pagination } = this.state
|
|
189
|
+
const { current, pageSize } = pagination
|
|
228
190
|
const data = this.getFilteredData()
|
|
229
191
|
|
|
230
192
|
if (data.length === 0) {
|
|
@@ -241,19 +203,29 @@ export default class SettingPasswords extends Component {
|
|
|
241
203
|
onChange: this.handleSearchChange,
|
|
242
204
|
placeholder: e('search')
|
|
243
205
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
206
|
+
|
|
207
|
+
// clamp current page when filtering shrinks the data set
|
|
208
|
+
const pageCount = Math.ceil(data.length / pageSize)
|
|
209
|
+
const safePage = Math.min(current, Math.max(pageCount, 1))
|
|
210
|
+
const pagedData = data.slice((safePage - 1) * pageSize, safePage * pageSize)
|
|
211
|
+
|
|
212
|
+
const paginationProps0 = {
|
|
213
|
+
total: data.length,
|
|
214
|
+
current: safePage,
|
|
215
|
+
pageSize,
|
|
216
|
+
showSizeChanger: true,
|
|
217
|
+
onChange: this.handlePageChange,
|
|
218
|
+
onShowSizeChange: this.handlePageChange,
|
|
250
219
|
size: 'small'
|
|
251
220
|
}
|
|
252
221
|
|
|
253
222
|
return (
|
|
254
223
|
<div>
|
|
255
224
|
<Search {...searchProps0} />
|
|
256
|
-
<
|
|
225
|
+
<div className='setting-passwords-list'>
|
|
226
|
+
{pagedData.map(item => this.renderItem(item))}
|
|
227
|
+
</div>
|
|
228
|
+
<Pagination {...paginationProps0} />
|
|
257
229
|
</div>
|
|
258
230
|
)
|
|
259
231
|
}
|
|
@@ -10,4 +10,40 @@
|
|
|
10
10
|
left 43px
|
|
11
11
|
height 60px
|
|
12
12
|
right 0
|
|
13
|
-
z-index 888
|
|
13
|
+
z-index 888
|
|
14
|
+
.deep-link-btns
|
|
15
|
+
display flex
|
|
16
|
+
flex-wrap wrap
|
|
17
|
+
gap 8px
|
|
18
|
+
|
|
19
|
+
// passwords list — replaces the old table so content wraps
|
|
20
|
+
// instead of forcing horizontal scroll on narrow/mobile screens
|
|
21
|
+
.setting-passwords-list
|
|
22
|
+
border 1px solid var(--main-light)
|
|
23
|
+
border-radius 6px
|
|
24
|
+
overflow hidden
|
|
25
|
+
margin 8px 0
|
|
26
|
+
.setting-passwords-item
|
|
27
|
+
padding 8px 12px
|
|
28
|
+
&:not(:last-child)
|
|
29
|
+
border-bottom 1px solid var(--main-light)
|
|
30
|
+
.setting-passwords-item
|
|
31
|
+
.setting-passwords-item-inner
|
|
32
|
+
display flex
|
|
33
|
+
flex-wrap wrap
|
|
34
|
+
align-items center
|
|
35
|
+
gap 8px
|
|
36
|
+
width 100%
|
|
37
|
+
.setting-passwords-pwd-cell
|
|
38
|
+
flex-shrink 0
|
|
39
|
+
.setting-passwords-count-cell
|
|
40
|
+
flex-shrink 0
|
|
41
|
+
margin 0
|
|
42
|
+
.setting-passwords-host-cell
|
|
43
|
+
flex 1 1 120px
|
|
44
|
+
min-width 120px
|
|
45
|
+
word-break break-all
|
|
46
|
+
line-height 1.5
|
|
47
|
+
.setting-passwords-actions-cell
|
|
48
|
+
flex-shrink 0
|
|
49
|
+
margin-left auto
|
|
@@ -9,6 +9,7 @@ import { Tabs, Tooltip } from 'antd'
|
|
|
9
9
|
import MultiSelectModal from '../common/multi-select-modal'
|
|
10
10
|
import {
|
|
11
11
|
ArrowsAltOutlined,
|
|
12
|
+
CloseOutlined,
|
|
12
13
|
EditOutlined,
|
|
13
14
|
PlusCircleOutlined,
|
|
14
15
|
ShrinkOutlined,
|
|
@@ -105,6 +106,10 @@ export default memo(function SidebarPanel (props) {
|
|
|
105
106
|
onClick={store.handlePin}
|
|
106
107
|
/>
|
|
107
108
|
</Tooltip>
|
|
109
|
+
<CloseOutlined
|
|
110
|
+
{...prps}
|
|
111
|
+
onClick={store.handleCloseSidebar}
|
|
112
|
+
/>
|
|
108
113
|
</div>
|
|
109
114
|
<div className='pd1y pd2x'>
|
|
110
115
|
<Tabs {...tabsProps} />
|
package/client/css/mobile.styl
CHANGED
|
@@ -100,3 +100,35 @@
|
|
|
100
100
|
margin-top 0
|
|
101
101
|
min-height 100vh
|
|
102
102
|
border-radius 0
|
|
103
|
+
|
|
104
|
+
// ---- 5. Sync panel data transport: stack export/import and auto-sync
|
|
105
|
+
// vertically with a gap. On narrow screens the right-floated
|
|
106
|
+
// auto-sync block wraps under the import button with no vertical
|
|
107
|
+
// gap — left-align it and add breathing room.
|
|
108
|
+
.sync-data-transport
|
|
109
|
+
.fleft,
|
|
110
|
+
.fright
|
|
111
|
+
float none
|
|
112
|
+
.fright
|
|
113
|
+
margin-top 8px
|
|
114
|
+
// the switch carries mg3l (32px left margin) to separate it from the
|
|
115
|
+
// import button on desktop — reset it when stacked so it left-aligns
|
|
116
|
+
.ant-switch
|
|
117
|
+
margin-left 0
|
|
118
|
+
|
|
119
|
+
// ---- 6. Solid Radio.Button groups (e.g. SSH auth type selector, session
|
|
120
|
+
// type selector): on narrow screens the connected button row wraps,
|
|
121
|
+
// but antd styles it as a single horizontal strip — adjacent buttons
|
|
122
|
+
// share borders via -1px margin and border-radius is only applied to
|
|
123
|
+
// the first/last child. When the row wraps, radius/borders break and
|
|
124
|
+
// the UI looks messed up. Convert each button to a standalone pill so
|
|
125
|
+
// wrapping stays tidy.
|
|
126
|
+
.ant-radio-group.ant-radio-group-solid
|
|
127
|
+
display flex
|
|
128
|
+
flex-wrap wrap
|
|
129
|
+
gap 6px
|
|
130
|
+
.ant-radio-button-wrapper
|
|
131
|
+
border-radius 3px
|
|
132
|
+
margin-inline-end 0
|
|
133
|
+
&::before
|
|
134
|
+
display none
|
package/client/store/sidebar.js
CHANGED
|
@@ -29,6 +29,15 @@ export default Store => {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
Store.prototype.handleCloseSidebar = function () {
|
|
33
|
+
const { store } = window
|
|
34
|
+
if (store.pinned) {
|
|
35
|
+
ls.setItem(sidebarPinnedKey, 'false')
|
|
36
|
+
store.pinned = false
|
|
37
|
+
}
|
|
38
|
+
store.setOpenedSideBar('')
|
|
39
|
+
}
|
|
40
|
+
|
|
32
41
|
Store.prototype.handleSidebarPanelTab = function (tab) {
|
|
33
42
|
window.store.sidebarPanelTab = tab
|
|
34
43
|
}
|