@electerm/electerm-react 1.39.99 → 1.39.103
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/common/constants.js +4 -2
- package/client/components/auth/login.jsx +6 -0
- package/client/components/auth/login.styl +7 -1
- package/client/components/bookmark-form/form-ssh-common.jsx +2 -2
- package/client/components/bookmark-form/local-form-ui.jsx +1 -1
- package/client/components/bookmark-form/rdp-form-ui.jsx +1 -1
- package/client/components/bookmark-form/render-auth-ssh.jsx +1 -1
- package/client/components/bookmark-form/render-delayed-scripts.jsx +1 -1
- package/client/components/bookmark-form/serial-form-ui.jsx +1 -1
- package/client/components/bookmark-form/vnc-form-ui.jsx +1 -1
- package/client/components/bookmark-form/web-form-ui.jsx +1 -1
- package/client/components/footer/batch-input.jsx +1 -1
- package/client/components/quick-commands/quick-commands-list-form.jsx +1 -1
- package/client/components/session/session.jsx +15 -2
- package/client/components/session/session.styl +5 -1
- package/client/components/setting-sync/setting-sync-form.jsx +64 -37
- package/client/components/tabs/index.jsx +4 -2
- package/client/store/setting.js +30 -2
- package/client/store/sync.js +10 -6
- package/package.json +1 -1
|
@@ -217,12 +217,14 @@ export const baseUpdateCheckUrls = [
|
|
|
217
217
|
export const syncTypes = buildConst([
|
|
218
218
|
'github',
|
|
219
219
|
'gitee',
|
|
220
|
-
'custom'
|
|
220
|
+
'custom',
|
|
221
|
+
'cloud'
|
|
221
222
|
])
|
|
222
223
|
export const syncTokenCreateUrls = {
|
|
223
224
|
gitee: 'https://gitee.com/github-zxdong262/electerm/wikis/Create%20personal%20access%20token?sort_id=3028409',
|
|
224
225
|
github: 'https://github.com/electerm/electerm/wiki/Create-personal-access-token',
|
|
225
|
-
custom: 'https://github.com/electerm/electerm/wiki/Custom-sync-server'
|
|
226
|
+
custom: 'https://github.com/electerm/electerm/wiki/Custom-sync-server',
|
|
227
|
+
cloud: 'https://electerm-cloud.html5beta.com'
|
|
226
228
|
}
|
|
227
229
|
export const settingSyncId = 'setting-sync'
|
|
228
230
|
export const settingTerminalId = 'setting-terminal'
|
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
ArrowRightOutlined
|
|
11
11
|
} from '@ant-design/icons'
|
|
12
12
|
import Main from '../main/main.jsx'
|
|
13
|
+
import AppDrag from '../tabs/app-drag'
|
|
14
|
+
import WindowControl from '../tabs/window-control'
|
|
13
15
|
import './login.styl'
|
|
14
16
|
|
|
15
17
|
const e = window.translate
|
|
@@ -90,6 +92,10 @@ export default class Login extends Component {
|
|
|
90
92
|
} = this.state
|
|
91
93
|
return (
|
|
92
94
|
<div className='login-wrap'>
|
|
95
|
+
<AppDrag />
|
|
96
|
+
<WindowControl
|
|
97
|
+
store={window.store}
|
|
98
|
+
/>
|
|
93
99
|
<div className='pd3 aligncenter'>
|
|
94
100
|
<LogoElem />
|
|
95
101
|
<div className='pd3 aligncenter'>
|
|
@@ -166,7 +166,7 @@ export default function renderCommon (props) {
|
|
|
166
166
|
name='description'
|
|
167
167
|
hasFeedback
|
|
168
168
|
>
|
|
169
|
-
<Input.TextArea
|
|
169
|
+
<Input.TextArea autoSize={{ minRows: 1 }} />
|
|
170
170
|
</FormItem>
|
|
171
171
|
<FormItem
|
|
172
172
|
{...formItemLayout}
|
|
@@ -196,7 +196,7 @@ export default function renderCommon (props) {
|
|
|
196
196
|
hasFeedback
|
|
197
197
|
>
|
|
198
198
|
<Input.TextArea
|
|
199
|
-
|
|
199
|
+
autoSize={{ minRows: 1 }}
|
|
200
200
|
/>
|
|
201
201
|
</FormItem>
|
|
202
202
|
{renderRunScripts()}
|
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
SearchOutlined,
|
|
14
14
|
FullscreenOutlined,
|
|
15
15
|
PaperClipOutlined,
|
|
16
|
-
CloseOutlined
|
|
16
|
+
CloseOutlined,
|
|
17
|
+
QuestionCircleOutlined
|
|
17
18
|
} from '@ant-design/icons'
|
|
18
19
|
import {
|
|
19
20
|
Tooltip
|
|
@@ -37,6 +38,7 @@ import ResizeWrap from '../common/resize-wrap'
|
|
|
37
38
|
import safeName from '../../common/safe-name'
|
|
38
39
|
import TerminalInfoContent from '../terminal-info/content'
|
|
39
40
|
import uid from '../../common/id-with-stamp'
|
|
41
|
+
import Link from '../common/external-link'
|
|
40
42
|
import postMessage from '../../common/post-msg'
|
|
41
43
|
import './session.styl'
|
|
42
44
|
|
|
@@ -541,7 +543,18 @@ export default class SessionWrapper extends Component {
|
|
|
541
543
|
if (isSsh || isLocal) {
|
|
542
544
|
controls.push(isSsh ? paneMap.sftp : paneMap.fileManager)
|
|
543
545
|
}
|
|
544
|
-
const checkTxt =
|
|
546
|
+
const checkTxt = (
|
|
547
|
+
<div>
|
|
548
|
+
<span>{e('sftpPathFollowSsh')}</span>
|
|
549
|
+
<span className='mg1l color-red'>[Beta]</span>
|
|
550
|
+
<Link
|
|
551
|
+
to='https://github.com/electerm/electerm/wiki/Warning-about-sftp-follow-ssh-path-function'
|
|
552
|
+
className='mg1l'
|
|
553
|
+
>
|
|
554
|
+
Wiki <QuestionCircleOutlined />
|
|
555
|
+
</Link>
|
|
556
|
+
</div>
|
|
557
|
+
)
|
|
545
558
|
const checkProps = {
|
|
546
559
|
onClick: this.toggleCheckSftpPathFollowSsh,
|
|
547
560
|
className: classnames(
|
|
@@ -24,8 +24,11 @@ export default function SyncForm (props) {
|
|
|
24
24
|
useConditionalEffect(() => {
|
|
25
25
|
form.resetFields()
|
|
26
26
|
}, delta && delta.prev && !eq(delta.prev, delta.curr))
|
|
27
|
-
|
|
27
|
+
const { syncType } = props
|
|
28
28
|
function disabled () {
|
|
29
|
+
if (syncType === syncTypes.cloud) {
|
|
30
|
+
return !props.formData.token
|
|
31
|
+
}
|
|
29
32
|
const {
|
|
30
33
|
token,
|
|
31
34
|
gistId
|
|
@@ -40,10 +43,15 @@ export default function SyncForm (props) {
|
|
|
40
43
|
}
|
|
41
44
|
if (res.gistId) {
|
|
42
45
|
up[syncType + 'GistId'] = res.gistId
|
|
46
|
+
} else if (syncType === syncTypes.cloud) {
|
|
47
|
+
up[syncType + 'GistId'] = 'cloud'
|
|
43
48
|
}
|
|
44
49
|
up[syncType + 'SyncPassword'] = res.syncPassword || ''
|
|
45
50
|
if (res.apiUrl) {
|
|
46
51
|
up[syncType + 'ApiUrl'] = res.apiUrl
|
|
52
|
+
} else if (syncType === syncTypes.cloud) {
|
|
53
|
+
up[syncType + 'ApiUrl'] = 'https://electerm-cloud.html5beta.com/api/sync'
|
|
54
|
+
// up[syncType + 'ApiUrl'] = 'http://127.0.0.1:5678/api/sync'
|
|
47
55
|
}
|
|
48
56
|
props.store.updateSyncSetting(up)
|
|
49
57
|
const test = await props.store.testSyncToken(syncType, res.gistId)
|
|
@@ -52,7 +60,7 @@ export default function SyncForm (props) {
|
|
|
52
60
|
message: 'token invalid'
|
|
53
61
|
})
|
|
54
62
|
}
|
|
55
|
-
if (!res.gistId) {
|
|
63
|
+
if (!res.gistId && syncType !== syncTypes.custom && syncType !== syncTypes.cloud) {
|
|
56
64
|
props.store.createGist(syncType)
|
|
57
65
|
}
|
|
58
66
|
}
|
|
@@ -93,7 +101,7 @@ export default function SyncForm (props) {
|
|
|
93
101
|
const {
|
|
94
102
|
lastSyncTime = ''
|
|
95
103
|
} = props.formData
|
|
96
|
-
|
|
104
|
+
|
|
97
105
|
const isCustom = syncType === syncTypes.custom
|
|
98
106
|
const timeFormatted = lastSyncTime
|
|
99
107
|
? dayjs(lastSyncTime).format('YYYY-MM-DD HH:mm:ss')
|
|
@@ -124,6 +132,15 @@ export default function SyncForm (props) {
|
|
|
124
132
|
return 'sync-input-' + name + '-' + syncType
|
|
125
133
|
}
|
|
126
134
|
function createUrlItem () {
|
|
135
|
+
if (syncType === syncTypes.cloud) {
|
|
136
|
+
return (
|
|
137
|
+
<p>
|
|
138
|
+
<Link to='https://electerm-cloud.html5beta.com'>
|
|
139
|
+
https://electerm-cloud.html5beta.com[Beta]
|
|
140
|
+
</Link>
|
|
141
|
+
</p>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
127
144
|
if (syncType !== syncTypes.custom) {
|
|
128
145
|
return null
|
|
129
146
|
}
|
|
@@ -152,31 +169,11 @@ export default function SyncForm (props) {
|
|
|
152
169
|
const gistLabel = createLabel('gist', idDesc)
|
|
153
170
|
const syncPasswordName = e('encrypt') + ' ' + e('password')
|
|
154
171
|
const syncPasswordLabel = createLabel(syncPasswordName, '')
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
name={'setting-sync-form' + syncType}
|
|
161
|
-
layout='vertical'
|
|
162
|
-
initialValues={props.formData}
|
|
163
|
-
>
|
|
164
|
-
{createUrlItem()}
|
|
165
|
-
<FormItem
|
|
166
|
-
label={tokenLabel}
|
|
167
|
-
hasFeedback
|
|
168
|
-
name='token'
|
|
169
|
-
rules={[{
|
|
170
|
-
max: 100, message: '100 chars max'
|
|
171
|
-
}, {
|
|
172
|
-
required: true, message: createPlaceHolder('token') + ' required'
|
|
173
|
-
}]}
|
|
174
|
-
>
|
|
175
|
-
<Input.Password
|
|
176
|
-
placeholder={createPlaceHolder('token')}
|
|
177
|
-
id={createId('token')}
|
|
178
|
-
/>
|
|
179
|
-
</FormItem>
|
|
172
|
+
function createIdItem () {
|
|
173
|
+
if (syncType === syncTypes.cloud) {
|
|
174
|
+
return null
|
|
175
|
+
}
|
|
176
|
+
return (
|
|
180
177
|
<FormItem
|
|
181
178
|
label={gistLabel}
|
|
182
179
|
name='gistId'
|
|
@@ -189,6 +186,13 @@ export default function SyncForm (props) {
|
|
|
189
186
|
id={createId('gistId')}
|
|
190
187
|
/>
|
|
191
188
|
</FormItem>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
function createPasswordItem () {
|
|
192
|
+
if (syncType === syncTypes.cloud) {
|
|
193
|
+
return null
|
|
194
|
+
}
|
|
195
|
+
return (
|
|
192
196
|
<FormItem
|
|
193
197
|
label={syncPasswordLabel}
|
|
194
198
|
hasFeedback
|
|
@@ -201,16 +205,39 @@ export default function SyncForm (props) {
|
|
|
201
205
|
placeholder={syncType + ' ' + syncPasswordName}
|
|
202
206
|
/>
|
|
203
207
|
</FormItem>
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
return (
|
|
211
|
+
<Form
|
|
212
|
+
onFinish={save}
|
|
213
|
+
form={form}
|
|
214
|
+
className='form-wrap pd1x'
|
|
215
|
+
name={'setting-sync-form' + syncType}
|
|
216
|
+
layout='vertical'
|
|
217
|
+
initialValues={props.formData}
|
|
218
|
+
>
|
|
219
|
+
{createUrlItem()}
|
|
220
|
+
<FormItem
|
|
221
|
+
label={tokenLabel}
|
|
222
|
+
hasFeedback
|
|
223
|
+
name='token'
|
|
224
|
+
rules={[{
|
|
225
|
+
max: 1100, message: '1100 chars max'
|
|
226
|
+
}, {
|
|
227
|
+
required: true, message: createPlaceHolder('token') + ' required'
|
|
228
|
+
}]}
|
|
207
229
|
>
|
|
208
|
-
<
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
onChange={this.onChangeAutoSync}
|
|
230
|
+
<Input.Password
|
|
231
|
+
placeholder={createPlaceHolder('token')}
|
|
232
|
+
id={createId('token')}
|
|
212
233
|
/>
|
|
213
|
-
</FormItem>
|
|
234
|
+
</FormItem>
|
|
235
|
+
{
|
|
236
|
+
createIdItem()
|
|
237
|
+
}
|
|
238
|
+
{
|
|
239
|
+
createPasswordItem()
|
|
240
|
+
}
|
|
214
241
|
<FormItem>
|
|
215
242
|
<p>
|
|
216
243
|
<Button
|
|
@@ -232,7 +259,7 @@ export default function SyncForm (props) {
|
|
|
232
259
|
type='dashed'
|
|
233
260
|
onClick={upload}
|
|
234
261
|
disabled={disabled()}
|
|
235
|
-
className='mg1r mg1b'
|
|
262
|
+
className='mg1r mg1b sync-btn-up'
|
|
236
263
|
icon={<ArrowUpOutlined />}
|
|
237
264
|
>{e('uploadSettings')}
|
|
238
265
|
</Button>
|
|
@@ -257,14 +257,16 @@ export default class Tabs extends React.Component {
|
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
renderContentInner () {
|
|
260
|
-
const { tabs = [], width } = this.props
|
|
260
|
+
const { tabs = [], width, config } = this.props
|
|
261
261
|
const len = tabs.length
|
|
262
262
|
const tabsWidthAll = tabMargin * len + 10 + this.tabsWidth()
|
|
263
263
|
const { overflow } = this.state
|
|
264
264
|
const left = overflow
|
|
265
265
|
? '100%'
|
|
266
266
|
: tabsWidthAll
|
|
267
|
-
const w1 = isMacJs && window.et.isWebApp
|
|
267
|
+
const w1 = isMacJs && (config.useSystemTitleBar || window.et.isWebApp)
|
|
268
|
+
? 30
|
|
269
|
+
: windowControlWidth
|
|
268
270
|
const style = {
|
|
269
271
|
width: width - w1 - 166
|
|
270
272
|
}
|
package/client/store/setting.js
CHANGED
|
@@ -94,17 +94,45 @@ export default Store => {
|
|
|
94
94
|
if (!item) {
|
|
95
95
|
return
|
|
96
96
|
}
|
|
97
|
+
|
|
97
98
|
store.addTab({
|
|
98
99
|
...item,
|
|
99
100
|
from: 'bookmarks',
|
|
100
101
|
srcId: item.id,
|
|
101
102
|
...newTerm(true, true)
|
|
102
103
|
})
|
|
103
|
-
|
|
104
|
+
|
|
104
105
|
if (store.config.disableSshHistory) {
|
|
105
106
|
return
|
|
106
107
|
}
|
|
107
|
-
|
|
108
|
+
|
|
109
|
+
// Critical Change: Use bookmarkId for matching instead of history id
|
|
110
|
+
const bookmarkId = item.id
|
|
111
|
+
const existingIndex = history.findIndex(h => h.bookmarkId === bookmarkId)
|
|
112
|
+
if (existingIndex >= 0) {
|
|
113
|
+
history[existingIndex].count = (history[existingIndex].count || 0) + 1
|
|
114
|
+
history[existingIndex].lastUse = Date.now()
|
|
115
|
+
const updatedItem = history.splice(existingIndex, 1)[0]
|
|
116
|
+
history.unshift(updatedItem)
|
|
117
|
+
} else {
|
|
118
|
+
const historyItem = {
|
|
119
|
+
...item,
|
|
120
|
+
id: generate(), // History item gets a unique id
|
|
121
|
+
bookmarkId, // Store original bookmark id for future matching
|
|
122
|
+
count: 1,
|
|
123
|
+
lastUse: Date.now()
|
|
124
|
+
}
|
|
125
|
+
history.unshift(historyItem)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
history.sort((a, b) => b.count - a.count || b.lastUse - a.lastUse)
|
|
129
|
+
|
|
130
|
+
// Optional: Consider max history length
|
|
131
|
+
const maxHistoryLength = store.config.maxHistoryLength || 50
|
|
132
|
+
if (history.length > maxHistoryLength) {
|
|
133
|
+
history.length = maxHistoryLength
|
|
134
|
+
}
|
|
135
|
+
|
|
108
136
|
store.setItems('history', history)
|
|
109
137
|
}
|
|
110
138
|
|
package/client/store/sync.js
CHANGED
|
@@ -50,8 +50,12 @@ export default (Store) => {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
Store.prototype.getSyncToken = function (type) {
|
|
53
|
-
if (type === syncTypes.custom) {
|
|
54
|
-
|
|
53
|
+
if (type === syncTypes.custom || type === syncTypes.cloud) {
|
|
54
|
+
const arr = ['AccessToken', 'ApiUrl']
|
|
55
|
+
if (type === syncTypes.custom) {
|
|
56
|
+
arr.push('GistId')
|
|
57
|
+
}
|
|
58
|
+
return arr.map(
|
|
55
59
|
p => {
|
|
56
60
|
return get(window.store.config, 'syncSetting.' + type + p)
|
|
57
61
|
}
|
|
@@ -149,11 +153,11 @@ export default (Store) => {
|
|
|
149
153
|
store.isSyncUpload = true
|
|
150
154
|
const token = store.getSyncToken(type)
|
|
151
155
|
let gistId = store.getSyncGistId(type)
|
|
152
|
-
if (!gistId) {
|
|
156
|
+
if (!gistId && type !== syncTypes.cloud && type !== syncTypes.custom) {
|
|
153
157
|
await store.createGist(type)
|
|
154
158
|
gistId = store.getSyncGistId(type)
|
|
155
159
|
}
|
|
156
|
-
if (!gistId) {
|
|
160
|
+
if (!gistId && type !== syncTypes.custom && type !== syncTypes.cloud) {
|
|
157
161
|
window.isSyncing = false
|
|
158
162
|
store.isSyncingSetting = false
|
|
159
163
|
store.isSyncUpload = false
|
|
@@ -218,11 +222,11 @@ export default (Store) => {
|
|
|
218
222
|
store.isSyncDownload = true
|
|
219
223
|
const token = store.getSyncToken(type)
|
|
220
224
|
let gistId = store.getSyncGistId(type)
|
|
221
|
-
if (!gistId) {
|
|
225
|
+
if (!gistId && type !== syncTypes.cloud && type !== syncTypes.custom) {
|
|
222
226
|
await store.createGist(type)
|
|
223
227
|
gistId = store.getSyncGistId(type)
|
|
224
228
|
}
|
|
225
|
-
if (!gistId) {
|
|
229
|
+
if (!gistId && type !== syncTypes.custom && type !== syncTypes.cloud) {
|
|
226
230
|
return
|
|
227
231
|
}
|
|
228
232
|
const pass = store.getSyncPassword(type)
|