@electerm/electerm-react 1.39.99 → 1.39.109
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/sftp/sftp-entry.jsx +1 -6
- package/client/components/tabs/index.jsx +4 -2
- package/client/store/setting.js +30 -2
- package/client/store/sync.js +26 -9
- 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>
|
|
@@ -30,7 +30,6 @@ import deepCopy from 'json-deep-copy'
|
|
|
30
30
|
import isValidPath from '../../common/is-valid-path'
|
|
31
31
|
import memoizeOne from 'memoize-one'
|
|
32
32
|
import postMessage from '../../common/post-msg'
|
|
33
|
-
import { runCmd } from '../terminal/terminal-apis'
|
|
34
33
|
import * as owner from './owner-list'
|
|
35
34
|
import AddressBar from './address-bar'
|
|
36
35
|
import getProxy from '../../common/get-proxy'
|
|
@@ -252,11 +251,7 @@ export default class Sftp extends Component {
|
|
|
252
251
|
if (this.props.sftpPathFollowSsh && this.props.cwd) {
|
|
253
252
|
return this.props.cwd
|
|
254
253
|
}
|
|
255
|
-
const home = await
|
|
256
|
-
this.props.pid,
|
|
257
|
-
this.props.sessionId,
|
|
258
|
-
'pwd'
|
|
259
|
-
).catch(window.store.onError)
|
|
254
|
+
const home = await this.sftp.getHomeDir()
|
|
260
255
|
if (home) {
|
|
261
256
|
return home.trim()
|
|
262
257
|
} else {
|
|
@@ -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
|
}
|
|
@@ -113,8 +117,21 @@ export default (Store) => {
|
|
|
113
117
|
}
|
|
114
118
|
|
|
115
119
|
Store.prototype.handleClearSyncSetting = async function () {
|
|
116
|
-
window
|
|
117
|
-
|
|
120
|
+
const { store } = window
|
|
121
|
+
const currentSyncType = store.syncType
|
|
122
|
+
const currentSettings = store.config.syncSetting || {}
|
|
123
|
+
console.log('currentSettings: ', currentSyncType, currentSettings)
|
|
124
|
+
// Create a new object without the current sync type's settings
|
|
125
|
+
const updatedSettings = Object.keys(currentSettings).reduce((acc, key) => {
|
|
126
|
+
if (!key.startsWith(currentSyncType)) {
|
|
127
|
+
acc[key] = currentSettings[key]
|
|
128
|
+
}
|
|
129
|
+
return acc
|
|
130
|
+
}, {})
|
|
131
|
+
console.log('updatedSettings: ', updatedSettings)
|
|
132
|
+
|
|
133
|
+
store.setConfig({
|
|
134
|
+
syncSetting: updatedSettings
|
|
118
135
|
})
|
|
119
136
|
}
|
|
120
137
|
|
|
@@ -149,11 +166,11 @@ export default (Store) => {
|
|
|
149
166
|
store.isSyncUpload = true
|
|
150
167
|
const token = store.getSyncToken(type)
|
|
151
168
|
let gistId = store.getSyncGistId(type)
|
|
152
|
-
if (!gistId) {
|
|
169
|
+
if (!gistId && type !== syncTypes.cloud && type !== syncTypes.custom) {
|
|
153
170
|
await store.createGist(type)
|
|
154
171
|
gistId = store.getSyncGistId(type)
|
|
155
172
|
}
|
|
156
|
-
if (!gistId) {
|
|
173
|
+
if (!gistId && type !== syncTypes.custom && type !== syncTypes.cloud) {
|
|
157
174
|
window.isSyncing = false
|
|
158
175
|
store.isSyncingSetting = false
|
|
159
176
|
store.isSyncUpload = false
|
|
@@ -218,11 +235,11 @@ export default (Store) => {
|
|
|
218
235
|
store.isSyncDownload = true
|
|
219
236
|
const token = store.getSyncToken(type)
|
|
220
237
|
let gistId = store.getSyncGistId(type)
|
|
221
|
-
if (!gistId) {
|
|
238
|
+
if (!gistId && type !== syncTypes.cloud && type !== syncTypes.custom) {
|
|
222
239
|
await store.createGist(type)
|
|
223
240
|
gistId = store.getSyncGistId(type)
|
|
224
241
|
}
|
|
225
|
-
if (!gistId) {
|
|
242
|
+
if (!gistId && type !== syncTypes.custom && type !== syncTypes.cloud) {
|
|
226
243
|
return
|
|
227
244
|
}
|
|
228
245
|
const pass = store.getSyncPassword(type)
|
|
@@ -342,7 +359,7 @@ export default (Store) => {
|
|
|
342
359
|
Store.prototype.handleExportAllData = async function () {
|
|
343
360
|
const { store } = window
|
|
344
361
|
const objs = {}
|
|
345
|
-
const names = store.getDataSyncNames(true)
|
|
362
|
+
const { names } = store.getDataSyncNames(true)
|
|
346
363
|
for (const n of names) {
|
|
347
364
|
objs[n] = store.getItems(n)
|
|
348
365
|
const order = await getData(`${n}:order`)
|