@electerm/electerm-react 1.38.11 → 1.38.30
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/build-ssh-tunnel.js +2 -1
- package/client/common/default-setting.js +9 -1
- package/client/components/bookmark-form/render-ssh-tunnel.jsx +32 -14
- package/client/components/main/main.jsx +4 -0
- package/client/components/quick-commands/quick-commands-box.jsx +20 -5
- package/client/components/quick-commands/quick-commands-form-elem.jsx +14 -17
- package/client/components/quick-commands/quick-commands-list-form.jsx +89 -0
- package/client/components/session/session.jsx +5 -4
- package/client/components/setting-panel/keywords-form.jsx +2 -1
- package/client/components/setting-panel/setting-terminal.jsx +5 -2
- package/client/components/setting-sync/setting-sync-form.jsx +1 -3
- package/client/components/sftp/file-item.jsx +27 -20
- package/client/components/sftp/sftp-entry.jsx +65 -45
- package/client/components/tabs/index.jsx +1 -1
- package/client/components/terminal/attach-addon-custom.js +2 -2
- package/client/components/terminal/index.jsx +3 -3
- package/client/components/terminal/xterm-zmodem.js +3 -3
- package/client/components/terminal-info/activity.jsx +2 -2
- package/client/components/terminal-info/base.jsx +47 -2
- package/client/components/terminal-info/disk.jsx +2 -2
- package/client/components/terminal-info/network.jsx +2 -2
- package/client/components/terminal-info/resource.jsx +20 -10
- package/client/components/terminal-info/up.jsx +2 -2
- package/client/css/basic.styl +3 -0
- package/client/store/common.js +51 -0
- package/client/store/system-menu.js +6 -16
- package/package.json +1 -1
|
@@ -2,6 +2,7 @@ export const buildSshTunnels = function (inst) {
|
|
|
2
2
|
return [{
|
|
3
3
|
sshTunnel: inst.sshTunnel,
|
|
4
4
|
sshTunnelRemotePort: inst.sshTunnelRemotePort,
|
|
5
|
-
sshTunnelLocalPort: inst.sshTunnelLocalPort
|
|
5
|
+
sshTunnelLocalPort: inst.sshTunnelLocalPort,
|
|
6
|
+
sshTunnelRemoteHost: inst.sshTunnelRemoteHost
|
|
6
7
|
}]
|
|
7
8
|
}
|
|
@@ -43,5 +43,13 @@ export default {
|
|
|
43
43
|
addTimeStampToTermLog: false,
|
|
44
44
|
sftpPathFollowSsh: false,
|
|
45
45
|
keepaliveInterval: 0,
|
|
46
|
-
backspaceMode: '^?'
|
|
46
|
+
backspaceMode: '^?',
|
|
47
|
+
terminalInfos: [
|
|
48
|
+
'uptime',
|
|
49
|
+
'cpu',
|
|
50
|
+
'mem',
|
|
51
|
+
'activities',
|
|
52
|
+
'network',
|
|
53
|
+
'disks'
|
|
54
|
+
]
|
|
47
55
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Form,
|
|
3
|
+
Input,
|
|
3
4
|
InputNumber,
|
|
4
5
|
Radio,
|
|
5
6
|
Space,
|
|
@@ -47,29 +48,46 @@ export default function renderSshTunnel () {
|
|
|
47
48
|
</RadioButton>
|
|
48
49
|
</RadioGroup>
|
|
49
50
|
</FormItem>
|
|
51
|
+
<Space.Compact className='mg2x'>
|
|
52
|
+
<FormItem
|
|
53
|
+
label={e('destination')}
|
|
54
|
+
name={[field.name, 'sshTunnelRemoteHost']}
|
|
55
|
+
initialValue='127.0.0.1'
|
|
56
|
+
required
|
|
57
|
+
>
|
|
58
|
+
<Input
|
|
59
|
+
className='compact-input'
|
|
60
|
+
placeholder={e('host')}
|
|
61
|
+
/>
|
|
62
|
+
</FormItem>
|
|
63
|
+
<FormItem
|
|
64
|
+
label=''
|
|
65
|
+
name={[field.name, 'sshTunnelRemotePort']}
|
|
66
|
+
initialValue={22}
|
|
67
|
+
required
|
|
68
|
+
>
|
|
69
|
+
<InputNumber
|
|
70
|
+
min={1}
|
|
71
|
+
max={65535}
|
|
72
|
+
// addonBefore={e('remotePort')}
|
|
73
|
+
className='compact-input'
|
|
74
|
+
placeholder={e('port')}
|
|
75
|
+
/>
|
|
76
|
+
</FormItem>
|
|
77
|
+
</Space.Compact>
|
|
50
78
|
<FormItem
|
|
51
|
-
label=''
|
|
52
|
-
name={[field.name, 'sshTunnelRemotePort']}
|
|
53
|
-
required
|
|
54
|
-
>
|
|
55
|
-
<InputNumber
|
|
56
|
-
min={1}
|
|
57
|
-
max={65535}
|
|
58
|
-
addonBefore={e('remotePort')}
|
|
59
|
-
className='compact-input'
|
|
60
|
-
/>
|
|
61
|
-
</FormItem>
|
|
62
|
-
<FormItem
|
|
63
|
-
label=''
|
|
79
|
+
label={e('localPort')}
|
|
64
80
|
name={[field.name, 'sshTunnelLocalPort']}
|
|
81
|
+
initialValue={22}
|
|
65
82
|
required
|
|
66
83
|
className='mg2x'
|
|
67
84
|
>
|
|
68
85
|
<InputNumber
|
|
69
86
|
min={1}
|
|
70
87
|
max={65535}
|
|
71
|
-
addonBefore={e('localPort')}
|
|
88
|
+
// addonBefore={e('localPort')}
|
|
72
89
|
className='compact-input'
|
|
90
|
+
placeholder={e('port')}
|
|
73
91
|
/>
|
|
74
92
|
</FormItem>
|
|
75
93
|
<Button
|
|
@@ -41,6 +41,7 @@ export default class Index extends Component {
|
|
|
41
41
|
ipcOnEvent('zoom-reset', store.onZoomReset)
|
|
42
42
|
ipcOnEvent('zoomin', store.onZoomIn)
|
|
43
43
|
ipcOnEvent('zoomout', store.onZoomout)
|
|
44
|
+
ipcOnEvent('confirm-exit', store.beforeExitApp)
|
|
44
45
|
|
|
45
46
|
document.addEventListener('drop', function (e) {
|
|
46
47
|
e.preventDefault()
|
|
@@ -51,6 +52,9 @@ export default class Index extends Component {
|
|
|
51
52
|
e.stopPropagation()
|
|
52
53
|
})
|
|
53
54
|
window.addEventListener('offline', store.setOffline)
|
|
55
|
+
if (window.et.isWebApp) {
|
|
56
|
+
window.onbeforeunload = store.beforeExit
|
|
57
|
+
}
|
|
54
58
|
store.isSencondInstance = window.pre.runSync('isSencondInstance')
|
|
55
59
|
store.initData()
|
|
56
60
|
store.checkForDbUpgrade()
|
|
@@ -8,7 +8,9 @@ import { find, sortBy } from 'lodash-es'
|
|
|
8
8
|
import { Button, Input, Select, Space } from 'antd'
|
|
9
9
|
import * as ls from '../../common/safe-local-storage'
|
|
10
10
|
import copy from 'json-deep-copy'
|
|
11
|
+
import generate from '../../common/uid'
|
|
11
12
|
import CmdItem from './quick-command-item'
|
|
13
|
+
import delay from '../../common/wait'
|
|
12
14
|
import {
|
|
13
15
|
EditOutlined,
|
|
14
16
|
CloseCircleOutlined,
|
|
@@ -46,7 +48,7 @@ export default class QuickCommandsFooterBox extends Component {
|
|
|
46
48
|
this.props.store.pinnedQuickCommandBar = !this.props.store.pinnedQuickCommandBar
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
handleSelect = (id) => {
|
|
51
|
+
handleSelect = async (id) => {
|
|
50
52
|
const {
|
|
51
53
|
store
|
|
52
54
|
} = this.props
|
|
@@ -57,11 +59,24 @@ export default class QuickCommandsFooterBox extends Component {
|
|
|
57
59
|
this.props.store.currentQuickCommands,
|
|
58
60
|
a => a.id === id
|
|
59
61
|
)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
const { runQuickCommand } = this.props.store
|
|
63
|
+
const qms = qm && qm.commands
|
|
64
|
+
? qm.commands
|
|
65
|
+
: (qm && qm.command
|
|
66
|
+
? [
|
|
67
|
+
{
|
|
68
|
+
command: qm.command,
|
|
69
|
+
id: generate(),
|
|
70
|
+
delay: 100
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
: []
|
|
74
|
+
)
|
|
75
|
+
for (const q of qms) {
|
|
62
76
|
const realCmd = isWin
|
|
63
|
-
?
|
|
64
|
-
:
|
|
77
|
+
? q.command.replace(/\n/g, '\n\r')
|
|
78
|
+
: q.command
|
|
79
|
+
await delay(q.delay || 100)
|
|
65
80
|
runQuickCommand(realCmd, qm.inputOnly)
|
|
66
81
|
store.editQuickCommand(qm.id, {
|
|
67
82
|
clickCount: ((qm.clickCount || 0) + 1)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Button,
|
|
1
|
+
import { Button, Switch, Form, message, Select } from 'antd'
|
|
2
2
|
import copy from 'json-deep-copy'
|
|
3
3
|
import generate from '../../common/uid'
|
|
4
4
|
import InputAutoFocus from '../common/input-auto-focus'
|
|
5
|
-
|
|
5
|
+
import renderQm from './quick-commands-list-form'
|
|
6
6
|
const FormItem = Form.Item
|
|
7
7
|
const { Option } = Select
|
|
8
8
|
const { prefix } = window
|
|
@@ -17,13 +17,13 @@ export default function QuickCommandForm (props) {
|
|
|
17
17
|
const { formData } = props
|
|
18
18
|
const {
|
|
19
19
|
name,
|
|
20
|
-
|
|
20
|
+
commands,
|
|
21
21
|
inputOnly,
|
|
22
22
|
labels
|
|
23
23
|
} = res
|
|
24
24
|
const update = copy({
|
|
25
25
|
name,
|
|
26
|
-
|
|
26
|
+
commands,
|
|
27
27
|
inputOnly,
|
|
28
28
|
labels
|
|
29
29
|
})
|
|
@@ -46,6 +46,13 @@ export default function QuickCommandForm (props) {
|
|
|
46
46
|
if (!initialValues.labels) {
|
|
47
47
|
initialValues.labels = []
|
|
48
48
|
}
|
|
49
|
+
if (!initialValues.commands) {
|
|
50
|
+
initialValues.commands = [{
|
|
51
|
+
command: initialValues.command || '',
|
|
52
|
+
id: generate(),
|
|
53
|
+
delay: 100
|
|
54
|
+
}]
|
|
55
|
+
}
|
|
49
56
|
return (
|
|
50
57
|
<Form
|
|
51
58
|
form={form}
|
|
@@ -59,7 +66,7 @@ export default function QuickCommandForm (props) {
|
|
|
59
66
|
rules={[{
|
|
60
67
|
max: 60, message: '60 chars max'
|
|
61
68
|
}, {
|
|
62
|
-
required: true, message: '
|
|
69
|
+
required: true, message: 'Name required'
|
|
63
70
|
}]}
|
|
64
71
|
hasFeedback
|
|
65
72
|
name='name'
|
|
@@ -69,17 +76,7 @@ export default function QuickCommandForm (props) {
|
|
|
69
76
|
autofocustrigger={autofocustrigger}
|
|
70
77
|
/>
|
|
71
78
|
</FormItem>
|
|
72
|
-
|
|
73
|
-
name='command'
|
|
74
|
-
label={t('quickCommand')}
|
|
75
|
-
rules={[{
|
|
76
|
-
max: 5000, message: '5000 chars max'
|
|
77
|
-
}, {
|
|
78
|
-
required: true, message: 'command required'
|
|
79
|
-
}]}
|
|
80
|
-
>
|
|
81
|
-
<TextArea rows={3} />
|
|
82
|
-
</FormItem>
|
|
79
|
+
{renderQm()}
|
|
83
80
|
<FormItem
|
|
84
81
|
name='labels'
|
|
85
82
|
label={t('label')}
|
|
@@ -108,7 +105,7 @@ export default function QuickCommandForm (props) {
|
|
|
108
105
|
<FormItem>
|
|
109
106
|
<p>
|
|
110
107
|
<Button
|
|
111
|
-
type='
|
|
108
|
+
type='primary'
|
|
112
109
|
htmlType='submit'
|
|
113
110
|
>{e('save')}
|
|
114
111
|
</Button>
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Form,
|
|
3
|
+
InputNumber,
|
|
4
|
+
Space,
|
|
5
|
+
Button,
|
|
6
|
+
Input
|
|
7
|
+
} from 'antd'
|
|
8
|
+
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'
|
|
9
|
+
import { formItemLayout } from '../../common/form-layout'
|
|
10
|
+
|
|
11
|
+
const FormItem = Form.Item
|
|
12
|
+
const FormList = Form.List
|
|
13
|
+
const { prefix } = window
|
|
14
|
+
const t = prefix('quickCommands')
|
|
15
|
+
|
|
16
|
+
export default function renderQm () {
|
|
17
|
+
function renderItem (field, i, add, remove) {
|
|
18
|
+
return (
|
|
19
|
+
<Space
|
|
20
|
+
align='center'
|
|
21
|
+
key={field.key}
|
|
22
|
+
>
|
|
23
|
+
<FormItem
|
|
24
|
+
label=''
|
|
25
|
+
name={[field.name, 'delay']}
|
|
26
|
+
required
|
|
27
|
+
>
|
|
28
|
+
<InputNumber
|
|
29
|
+
min={1}
|
|
30
|
+
step={1}
|
|
31
|
+
max={65535}
|
|
32
|
+
addonBefore={t('delay')}
|
|
33
|
+
placeholder={100}
|
|
34
|
+
className='compact-input'
|
|
35
|
+
/>
|
|
36
|
+
</FormItem>
|
|
37
|
+
<FormItem
|
|
38
|
+
label=''
|
|
39
|
+
name={[field.name, 'command']}
|
|
40
|
+
required
|
|
41
|
+
className='mg2x'
|
|
42
|
+
>
|
|
43
|
+
<Input.TextArea
|
|
44
|
+
rows={1}
|
|
45
|
+
placeholder={t('quickCommand')}
|
|
46
|
+
className='compact-input'
|
|
47
|
+
/>
|
|
48
|
+
</FormItem>
|
|
49
|
+
<Button
|
|
50
|
+
icon={<MinusCircleOutlined />}
|
|
51
|
+
onClick={() => remove(field.name)}
|
|
52
|
+
className='mg24b'
|
|
53
|
+
/>
|
|
54
|
+
</Space>
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<FormItem {...formItemLayout} label={t('quickCommands')}>
|
|
60
|
+
<FormList
|
|
61
|
+
name='commands'
|
|
62
|
+
>
|
|
63
|
+
{
|
|
64
|
+
(fields, { add, remove }, { errors }) => {
|
|
65
|
+
return (
|
|
66
|
+
<div>
|
|
67
|
+
{
|
|
68
|
+
fields.map((field, i) => {
|
|
69
|
+
return renderItem(field, i, add, remove)
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
<FormItem>
|
|
73
|
+
<Button
|
|
74
|
+
type='dashed'
|
|
75
|
+
onClick={() => add()}
|
|
76
|
+
block
|
|
77
|
+
icon={<PlusOutlined />}
|
|
78
|
+
>
|
|
79
|
+
{t('quickCommand')}
|
|
80
|
+
</Button>
|
|
81
|
+
</FormItem>
|
|
82
|
+
</div>
|
|
83
|
+
)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
</FormList>
|
|
87
|
+
</FormItem>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -472,10 +472,11 @@ export default class SessionWrapper extends Component {
|
|
|
472
472
|
renderControl = () => {
|
|
473
473
|
const { splitDirection, terminals, sftpPathFollowSsh } = this.state
|
|
474
474
|
const { props } = this
|
|
475
|
-
const { pane } = props.tab
|
|
475
|
+
const { pane, enableSsh } = props.tab
|
|
476
|
+
|
|
476
477
|
const termType = props.tab?.type
|
|
477
478
|
const isSsh = props.tab.authType
|
|
478
|
-
const isLocal = termType === connectionMap.local || !termType
|
|
479
|
+
const isLocal = !isSsh && (termType === connectionMap.local || !termType)
|
|
479
480
|
const isHori = splitDirection === terminalSplitDirectionMap.horizontal
|
|
480
481
|
const cls1 = 'mg1r icon-split pointer iblock spliter'
|
|
481
482
|
const cls2 = 'icon-direction pointer iblock spliter'
|
|
@@ -542,7 +543,7 @@ export default class SessionWrapper extends Component {
|
|
|
542
543
|
}
|
|
543
544
|
</div>
|
|
544
545
|
{
|
|
545
|
-
isSsh || isLocal
|
|
546
|
+
(isSsh && enableSsh) || isLocal
|
|
546
547
|
? (
|
|
547
548
|
<Tooltip title={checkTxt}>
|
|
548
549
|
<span {...checkProps}>
|
|
@@ -608,7 +609,7 @@ export default class SessionWrapper extends Component {
|
|
|
608
609
|
const { pane } = this.props.tab
|
|
609
610
|
const infoProps = {
|
|
610
611
|
infoPanelPinned,
|
|
611
|
-
...pick(this.props.config, ['host', 'port', 'saveTerminalLogToFile']),
|
|
612
|
+
...pick(this.props.config, ['host', 'port', 'saveTerminalLogToFile', 'terminalInfos']),
|
|
612
613
|
...infoPanelProps,
|
|
613
614
|
appPath: this.props.appPath,
|
|
614
615
|
rightSidebarWidth: this.props.rightSidebarWidth,
|
|
@@ -71,6 +71,7 @@ export default function KeywordForm (props) {
|
|
|
71
71
|
|
|
72
72
|
function renderBefore (name) {
|
|
73
73
|
const colors = ['red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white']
|
|
74
|
+
const { themeConfig } = props
|
|
74
75
|
return (
|
|
75
76
|
<FormItem name={[name, 'color']} noStyle>
|
|
76
77
|
<Select>
|
|
@@ -79,7 +80,7 @@ export default function KeywordForm (props) {
|
|
|
79
80
|
const ps = {
|
|
80
81
|
className: 'color-picker-choose iblock',
|
|
81
82
|
style: {
|
|
82
|
-
backgroundColor: color
|
|
83
|
+
backgroundColor: themeConfig[color]
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
return (
|
|
@@ -408,13 +408,15 @@ export default class SettingTerminal extends Component {
|
|
|
408
408
|
keywords = [{ color: 'red' }]
|
|
409
409
|
} = this.props.config
|
|
410
410
|
const {
|
|
411
|
-
appPath
|
|
411
|
+
appPath,
|
|
412
|
+
getThemeConfig
|
|
412
413
|
} = this.props.store
|
|
413
414
|
const ps = {
|
|
414
415
|
formData: {
|
|
415
416
|
keywords
|
|
416
417
|
},
|
|
417
|
-
submit: this.handleSubmitKeywords
|
|
418
|
+
submit: this.handleSubmitKeywords,
|
|
419
|
+
themeConfig: getThemeConfig()
|
|
418
420
|
}
|
|
419
421
|
const terminalLogPath = appPath
|
|
420
422
|
? osResolve(appPath, 'electerm', 'session_logs')
|
|
@@ -496,6 +498,7 @@ export default class SettingTerminal extends Component {
|
|
|
496
498
|
{this.renderToggle('saveTerminalLogToFile', (
|
|
497
499
|
<ShowItem to={terminalLogPath} className='mg1l'>{p('open')}</ShowItem>
|
|
498
500
|
))}
|
|
501
|
+
{this.renderToggle('addTimeStampToTermLog')}
|
|
499
502
|
{
|
|
500
503
|
[
|
|
501
504
|
'cursorBlink',
|
|
@@ -45,9 +45,7 @@ export default function SyncForm (props) {
|
|
|
45
45
|
if (res.gistId) {
|
|
46
46
|
up[syncType + 'GistId'] = res.gistId
|
|
47
47
|
}
|
|
48
|
-
|
|
49
|
-
up[syncType + 'SyncPassword'] = res.syncPassword
|
|
50
|
-
}
|
|
48
|
+
up[syncType + 'SyncPassword'] = res.syncPassword || ''
|
|
51
49
|
if (res.apiUrl) {
|
|
52
50
|
up[syncType + 'ApiUrl'] = res.apiUrl
|
|
53
51
|
}
|
|
@@ -761,13 +761,15 @@ export default class FileSection extends React.Component {
|
|
|
761
761
|
transferOrEnterDirectory = async (e, edit) => {
|
|
762
762
|
const { file } = this.state
|
|
763
763
|
const { isDirectory, type, size } = file
|
|
764
|
+
const isLocal = type === typeMap.local
|
|
765
|
+
const isRemote = type === typeMap.remote
|
|
764
766
|
if (isDirectory) {
|
|
765
767
|
return this.enterDirectory(e)
|
|
766
768
|
}
|
|
767
|
-
if (!edit &&
|
|
769
|
+
if (!edit && isLocal) {
|
|
768
770
|
return this.openFile(this.state.file)
|
|
769
771
|
}
|
|
770
|
-
const remoteEdit = !edit &&
|
|
772
|
+
const remoteEdit = !edit && isRemote && size < maxEditFileSize
|
|
771
773
|
if (
|
|
772
774
|
edit === true || remoteEdit
|
|
773
775
|
) {
|
|
@@ -787,13 +789,14 @@ export default class FileSection extends React.Component {
|
|
|
787
789
|
operation
|
|
788
790
|
) => {
|
|
789
791
|
const { name, path, type, isDirectory } = file
|
|
790
|
-
|
|
792
|
+
const isLocal = type === typeMap.local
|
|
793
|
+
let typeTo = isLocal
|
|
791
794
|
? typeMap.remote
|
|
792
795
|
: typeMap.local
|
|
793
796
|
if (_typeTo) {
|
|
794
797
|
typeTo = _typeTo
|
|
795
798
|
}
|
|
796
|
-
let toPath =
|
|
799
|
+
let toPath = isLocal
|
|
797
800
|
? this.props[typeMap.remote + 'Path']
|
|
798
801
|
: this.props[typeMap.local + 'Path']
|
|
799
802
|
if (toPathBase) {
|
|
@@ -928,10 +931,12 @@ export default class FileSection extends React.Component {
|
|
|
928
931
|
} = this.props
|
|
929
932
|
const hasHost = !!tab.host
|
|
930
933
|
const { enableSsh } = tab
|
|
931
|
-
const
|
|
934
|
+
const isLocal = type === typeMap.local
|
|
935
|
+
const isRemote = type === typeMap.remote
|
|
936
|
+
const transferText = isLocal
|
|
932
937
|
? e(transferTypeMap.upload)
|
|
933
938
|
: e(transferTypeMap.download)
|
|
934
|
-
const iconType =
|
|
939
|
+
const iconType = isLocal
|
|
935
940
|
? 'CloudUploadOutlined'
|
|
936
941
|
: 'CloudDownloadOutlined'
|
|
937
942
|
const len = selectedFiles.length
|
|
@@ -960,8 +965,8 @@ export default class FileSection extends React.Component {
|
|
|
960
965
|
if (
|
|
961
966
|
isDirectory && id &&
|
|
962
967
|
(
|
|
963
|
-
(hasHost && enableSsh &&
|
|
964
|
-
(
|
|
968
|
+
(hasHost && enableSsh && isRemote) ||
|
|
969
|
+
(isLocal && !hasHost)
|
|
965
970
|
)
|
|
966
971
|
) {
|
|
967
972
|
res.push({
|
|
@@ -977,14 +982,14 @@ export default class FileSection extends React.Component {
|
|
|
977
982
|
text: transferText
|
|
978
983
|
})
|
|
979
984
|
}
|
|
980
|
-
if (!isDirectory && id &&
|
|
985
|
+
if (!isDirectory && id && isLocal) {
|
|
981
986
|
res.push({
|
|
982
987
|
func: 'transferOrEnterDirectory',
|
|
983
988
|
icon: 'ArrowRightOutlined',
|
|
984
989
|
text: e('open')
|
|
985
990
|
})
|
|
986
991
|
}
|
|
987
|
-
if (id &&
|
|
992
|
+
if (id && isLocal) {
|
|
988
993
|
res.push({
|
|
989
994
|
func: 'showInDefaultFileManager',
|
|
990
995
|
icon: 'ContainerOutlined',
|
|
@@ -1040,16 +1045,18 @@ export default class FileSection extends React.Component {
|
|
|
1040
1045
|
text: m('copyFilePath')
|
|
1041
1046
|
})
|
|
1042
1047
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1048
|
+
if (enableSsh || isLocal) {
|
|
1049
|
+
res.push({
|
|
1050
|
+
func: 'newFile',
|
|
1051
|
+
icon: 'FileAddOutlined',
|
|
1052
|
+
text: e('newFile')
|
|
1053
|
+
})
|
|
1054
|
+
res.push({
|
|
1055
|
+
func: 'newDirectory',
|
|
1056
|
+
icon: 'FolderAddOutlined',
|
|
1057
|
+
text: e('newFolder')
|
|
1058
|
+
})
|
|
1059
|
+
}
|
|
1053
1060
|
res.push({
|
|
1054
1061
|
func: 'selectAll',
|
|
1055
1062
|
icon: 'CheckSquareOutlined',
|
|
@@ -562,6 +562,22 @@ export default class Sftp extends Component {
|
|
|
562
562
|
})
|
|
563
563
|
}
|
|
564
564
|
|
|
565
|
+
sftpList = (sftp, remotePath) => {
|
|
566
|
+
return sftp.list(remotePath)
|
|
567
|
+
.then(arr => {
|
|
568
|
+
return arr.map(item => {
|
|
569
|
+
const { type } = item
|
|
570
|
+
return {
|
|
571
|
+
...pick(item, ['name', 'size', 'accessTime', 'modifyTime', 'mode', 'owner', 'group']),
|
|
572
|
+
isDirectory: type === fileTypeMap.directory,
|
|
573
|
+
type: typeMap.remote,
|
|
574
|
+
path: remotePath,
|
|
575
|
+
id: generate()
|
|
576
|
+
}
|
|
577
|
+
})
|
|
578
|
+
})
|
|
579
|
+
}
|
|
580
|
+
|
|
565
581
|
remoteList = async (
|
|
566
582
|
returnList = false,
|
|
567
583
|
remotePathReal,
|
|
@@ -644,52 +660,8 @@ export default class Sftp extends Component {
|
|
|
644
660
|
}
|
|
645
661
|
}
|
|
646
662
|
|
|
647
|
-
|
|
663
|
+
const remote = await this.sftpList(sftp, remotePath)
|
|
648
664
|
this.sftp = sftp
|
|
649
|
-
const remotes = deepCopy(remote)
|
|
650
|
-
remote = []
|
|
651
|
-
for (const _r of remotes) {
|
|
652
|
-
const r = _r
|
|
653
|
-
const { type, name } = r
|
|
654
|
-
const f = {
|
|
655
|
-
...pick(r, ['name', 'size', 'accessTime', 'modifyTime', 'mode', 'owner', 'group']),
|
|
656
|
-
isDirectory: r.type === fileTypeMap.directory,
|
|
657
|
-
type: typeMap.remote,
|
|
658
|
-
path: remotePath,
|
|
659
|
-
id: generate()
|
|
660
|
-
}
|
|
661
|
-
if (type === fileTypeMap.link) {
|
|
662
|
-
const linkPath = resolve(remotePath, name)
|
|
663
|
-
let realpath = await sftp.readlink(linkPath)
|
|
664
|
-
.catch(e => {
|
|
665
|
-
console.debug(e)
|
|
666
|
-
return null
|
|
667
|
-
})
|
|
668
|
-
if (!realpath) {
|
|
669
|
-
continue
|
|
670
|
-
}
|
|
671
|
-
if (!isAbsPath(realpath)) {
|
|
672
|
-
realpath = resolve(remotePath, realpath)
|
|
673
|
-
realpath = await sftp.realpath(realpath)
|
|
674
|
-
}
|
|
675
|
-
const realFileInfo = await getRemoteFileInfo(
|
|
676
|
-
sftp,
|
|
677
|
-
realpath
|
|
678
|
-
).catch(e => {
|
|
679
|
-
log.debug('seems a bad symbolic link')
|
|
680
|
-
log.debug(e)
|
|
681
|
-
return null
|
|
682
|
-
})
|
|
683
|
-
if (!realFileInfo) {
|
|
684
|
-
continue
|
|
685
|
-
}
|
|
686
|
-
f.isSymbolicLink = true
|
|
687
|
-
f.isDirectory = realFileInfo.isDirectory
|
|
688
|
-
} else {
|
|
689
|
-
f.isSymbolicLink = false
|
|
690
|
-
}
|
|
691
|
-
remote.push(f)
|
|
692
|
-
}
|
|
693
665
|
const update = {
|
|
694
666
|
remote,
|
|
695
667
|
remoteFileTree: buildTree(remote),
|
|
@@ -712,6 +684,7 @@ export default class Sftp extends Component {
|
|
|
712
684
|
]).slice(0, maxSftpHistory)
|
|
713
685
|
}
|
|
714
686
|
this.setState(update, () => {
|
|
687
|
+
this.updateRemoteList(remote, remotePath, sftp)
|
|
715
688
|
this.props.editTab(tab.id, {
|
|
716
689
|
sftpCreated: true
|
|
717
690
|
})
|
|
@@ -731,6 +704,53 @@ export default class Sftp extends Component {
|
|
|
731
704
|
}
|
|
732
705
|
}
|
|
733
706
|
|
|
707
|
+
updateRemoteList = async (
|
|
708
|
+
remotes,
|
|
709
|
+
remotePath,
|
|
710
|
+
sftp
|
|
711
|
+
) => {
|
|
712
|
+
const remote = []
|
|
713
|
+
for (const r of remotes) {
|
|
714
|
+
const { type, name } = r
|
|
715
|
+
if (type === fileTypeMap.link) {
|
|
716
|
+
const linkPath = resolve(remotePath, name)
|
|
717
|
+
let realpath = await sftp.readlink(linkPath)
|
|
718
|
+
.catch(e => {
|
|
719
|
+
console.debug(e)
|
|
720
|
+
return null
|
|
721
|
+
})
|
|
722
|
+
if (!realpath) {
|
|
723
|
+
continue
|
|
724
|
+
}
|
|
725
|
+
if (!isAbsPath(realpath)) {
|
|
726
|
+
realpath = resolve(remotePath, realpath)
|
|
727
|
+
realpath = await sftp.realpath(realpath)
|
|
728
|
+
}
|
|
729
|
+
const realFileInfo = await getRemoteFileInfo(
|
|
730
|
+
sftp,
|
|
731
|
+
realpath
|
|
732
|
+
).catch(e => {
|
|
733
|
+
log.debug('seems a bad symbolic link')
|
|
734
|
+
log.debug(e)
|
|
735
|
+
return null
|
|
736
|
+
})
|
|
737
|
+
if (!realFileInfo) {
|
|
738
|
+
continue
|
|
739
|
+
}
|
|
740
|
+
r.isSymbolicLink = true
|
|
741
|
+
r.isDirectory = realFileInfo.isDirectory
|
|
742
|
+
} else {
|
|
743
|
+
r.isSymbolicLink = false
|
|
744
|
+
}
|
|
745
|
+
remote.push(r)
|
|
746
|
+
}
|
|
747
|
+
const update = {
|
|
748
|
+
remote,
|
|
749
|
+
remoteFileTree: buildTree(remote)
|
|
750
|
+
}
|
|
751
|
+
this.setState(update)
|
|
752
|
+
}
|
|
753
|
+
|
|
734
754
|
localList = async (returnList = false, localPathReal, oldPath) => {
|
|
735
755
|
if (!fs) return
|
|
736
756
|
if (!returnList) {
|
|
@@ -75,7 +75,7 @@ export default class Tabs extends React.Component {
|
|
|
75
75
|
const len = tabs.length
|
|
76
76
|
const addBtnWidth = 22
|
|
77
77
|
const tabsWidth = this.tabsWidth()
|
|
78
|
-
const tabsWidthAll = tabMargin * len +
|
|
78
|
+
const tabsWidthAll = tabMargin * len + 166 + tabsWidth
|
|
79
79
|
return width < (tabsWidthAll + addBtnWidth)
|
|
80
80
|
}
|
|
81
81
|
|
|
@@ -55,7 +55,7 @@ export default class AttachAddonCustom extends AttachAddon {
|
|
|
55
55
|
const data = ev.target.result
|
|
56
56
|
const { term } = this
|
|
57
57
|
const str = this.decoder.decode(data)
|
|
58
|
-
if (term
|
|
58
|
+
if (term?.parent?.props.sftpPathFollowSsh && term?.buffer.active.type !== 'alternate') {
|
|
59
59
|
const {
|
|
60
60
|
cwdId
|
|
61
61
|
} = term
|
|
@@ -78,7 +78,7 @@ export default class AttachAddonCustom extends AttachAddon {
|
|
|
78
78
|
}
|
|
79
79
|
term.write(nnss.join('\r'))
|
|
80
80
|
} else {
|
|
81
|
-
term
|
|
81
|
+
term?.write(str)
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -613,7 +613,7 @@ class Term extends Component {
|
|
|
613
613
|
title: 'Choose some files to send',
|
|
614
614
|
message: 'Choose some files to send',
|
|
615
615
|
properties
|
|
616
|
-
})
|
|
616
|
+
}).catch(() => false)
|
|
617
617
|
if (!files || !files.length) {
|
|
618
618
|
return this.onZmodemEnd()
|
|
619
619
|
}
|
|
@@ -637,7 +637,7 @@ class Term extends Component {
|
|
|
637
637
|
'treatPackageAsDirectory',
|
|
638
638
|
'dontAddToRecent'
|
|
639
639
|
]
|
|
640
|
-
})
|
|
640
|
+
}).catch(() => false)
|
|
641
641
|
if (!savePaths || !savePaths.length) {
|
|
642
642
|
return false
|
|
643
643
|
}
|
|
@@ -1203,7 +1203,7 @@ class Term extends Component {
|
|
|
1203
1203
|
}
|
|
1204
1204
|
|
|
1205
1205
|
oncloseSocket = () => {
|
|
1206
|
-
if (this.onClose) {
|
|
1206
|
+
if (this.onClose || !this.props.tab.enableSsh) {
|
|
1207
1207
|
return
|
|
1208
1208
|
}
|
|
1209
1209
|
this.setStatus(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import zmodem from 'zmodem
|
|
2
|
-
|
|
1
|
+
// import zmodem from 'zmodem-ts/dist/zmodem.mjs'
|
|
2
|
+
import Sentry from 'zmodem-ts/dist/zsentry.js'
|
|
3
3
|
export class AddonZmodem {
|
|
4
4
|
_disposables = []
|
|
5
5
|
|
|
@@ -20,7 +20,7 @@ export class AddonZmodem {
|
|
|
20
20
|
this.socket = ctx.socket
|
|
21
21
|
this.term = ctx.term
|
|
22
22
|
this.ctx = ctx
|
|
23
|
-
this.zsentry = new
|
|
23
|
+
this.zsentry = new Sentry({
|
|
24
24
|
to_terminal: (octets) => {
|
|
25
25
|
if (ctx.onZmodem) {
|
|
26
26
|
this.term.write(String.fromCharCode.apply(String, octets))
|
|
@@ -11,8 +11,8 @@ const { prefix } = window
|
|
|
11
11
|
const m = prefix('menu')
|
|
12
12
|
|
|
13
13
|
export default function TerminalInfoActivities (props) {
|
|
14
|
-
const { activities } = props
|
|
15
|
-
if (isEmpty(activities) || !
|
|
14
|
+
const { activities, isRemote, terminalInfos } = props
|
|
15
|
+
if (isEmpty(activities) || !isRemote || !terminalInfos.includes('activities')) {
|
|
16
16
|
return null
|
|
17
17
|
}
|
|
18
18
|
const col = colsParser(activities[0])
|
|
@@ -7,9 +7,12 @@ import {
|
|
|
7
7
|
commonActions
|
|
8
8
|
} from '../../common/constants'
|
|
9
9
|
import {
|
|
10
|
-
Switch
|
|
10
|
+
Switch,
|
|
11
|
+
Space,
|
|
12
|
+
Button
|
|
11
13
|
} from 'antd'
|
|
12
14
|
import ShowItem from '../common/show-item'
|
|
15
|
+
import defaults from '../../common/default-setting'
|
|
13
16
|
import postMsg from '../../common/post-msg'
|
|
14
17
|
import { toggleTerminalLog, toggleTerminalLogTimestamp } from '../terminal/terminal-apis'
|
|
15
18
|
|
|
@@ -54,6 +57,16 @@ export default class TerminalInfoBase extends Component {
|
|
|
54
57
|
})
|
|
55
58
|
}
|
|
56
59
|
|
|
60
|
+
toggleTerminalLogInfo = (h) => {
|
|
61
|
+
const { terminalInfos } = this.props
|
|
62
|
+
const nv = terminalInfos.includes(h)
|
|
63
|
+
? terminalInfos.filter(f => f !== h)
|
|
64
|
+
: [...terminalInfos, h]
|
|
65
|
+
window.store.setConfig({
|
|
66
|
+
terminalInfos: nv
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
57
70
|
handleToggle = () => {
|
|
58
71
|
const { saveTerminalLogToFile, addTimeStampToTermLog } = this.state
|
|
59
72
|
const {
|
|
@@ -120,10 +133,37 @@ export default class TerminalInfoBase extends Component {
|
|
|
120
133
|
unCheckedChildren={name}
|
|
121
134
|
checked={addTimeStampToTermLog}
|
|
122
135
|
onChange={this.handleToggleTimestamp}
|
|
136
|
+
className='mg1b'
|
|
123
137
|
/>
|
|
124
138
|
)
|
|
125
139
|
}
|
|
126
140
|
|
|
141
|
+
renderInfoSelection () {
|
|
142
|
+
const {
|
|
143
|
+
terminalInfos
|
|
144
|
+
} = this.props
|
|
145
|
+
return (
|
|
146
|
+
<Space.Compact block>
|
|
147
|
+
{
|
|
148
|
+
defaults.terminalInfos.map(f => {
|
|
149
|
+
const type = terminalInfos.includes(f) ? 'primary' : 'default'
|
|
150
|
+
return (
|
|
151
|
+
<Button
|
|
152
|
+
key={f + 'term-info-sel'}
|
|
153
|
+
type={type}
|
|
154
|
+
size='small'
|
|
155
|
+
onClick={() => this.toggleTerminalLogInfo(f)}
|
|
156
|
+
className='cap'
|
|
157
|
+
>
|
|
158
|
+
{f}
|
|
159
|
+
</Button>
|
|
160
|
+
)
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
</Space.Compact>
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
|
|
127
167
|
render () {
|
|
128
168
|
const {
|
|
129
169
|
id,
|
|
@@ -149,13 +189,18 @@ export default class TerminalInfoBase extends Component {
|
|
|
149
189
|
unCheckedChildren={name}
|
|
150
190
|
checked={saveTerminalLogToFile}
|
|
151
191
|
onChange={this.handleToggle}
|
|
152
|
-
className='mg1r'
|
|
192
|
+
className='mg1r mg1b'
|
|
153
193
|
/>
|
|
154
194
|
{
|
|
155
195
|
this.renderTimestamp()
|
|
156
196
|
}
|
|
157
197
|
</span>
|
|
158
198
|
</div>
|
|
199
|
+
<div className='pd1y'>
|
|
200
|
+
{
|
|
201
|
+
this.renderInfoSelection()
|
|
202
|
+
}
|
|
203
|
+
</div>
|
|
159
204
|
<p><b>log:</b> {to}</p>
|
|
160
205
|
</div>
|
|
161
206
|
)
|
|
@@ -7,8 +7,8 @@ import { isEmpty } from 'lodash-es'
|
|
|
7
7
|
import colsParser from './data-cols-parser'
|
|
8
8
|
|
|
9
9
|
export default function TerminalInfoDisk (props) {
|
|
10
|
-
const { disks } = props
|
|
11
|
-
if (isEmpty(disks) || !
|
|
10
|
+
const { disks, isRemote, terminalInfos } = props
|
|
11
|
+
if (isEmpty(disks) || !isRemote || !terminalInfos.includes('disks')) {
|
|
12
12
|
return null
|
|
13
13
|
}
|
|
14
14
|
const col = colsParser(disks[0])
|
|
@@ -9,8 +9,8 @@ import { formatBytes } from '../../common/byte-format'
|
|
|
9
9
|
import copy from 'json-deep-copy'
|
|
10
10
|
|
|
11
11
|
export default function TerminalInfoDisk (props) {
|
|
12
|
-
const { network } = props
|
|
13
|
-
if (isEmpty(network) || !
|
|
12
|
+
const { network, isRemote, terminalInfos } = props
|
|
13
|
+
if (isEmpty(network) || !isRemote || !terminalInfos.includes('network')) {
|
|
14
14
|
return null
|
|
15
15
|
}
|
|
16
16
|
const [state, setter] = useState({
|
|
@@ -25,8 +25,13 @@ function computePercent (used, total) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export default function TerminalInfoResource (props) {
|
|
28
|
-
const { cpu, mem, swap } = props
|
|
29
|
-
if (
|
|
28
|
+
const { cpu, mem, swap, isRemote, terminalInfos } = props
|
|
29
|
+
if (
|
|
30
|
+
!isRemote ||
|
|
31
|
+
(!terminalInfos.includes('cpu') &&
|
|
32
|
+
!terminalInfos.includes('mem') &&
|
|
33
|
+
!terminalInfos.includes('swap'))
|
|
34
|
+
) {
|
|
30
35
|
return null
|
|
31
36
|
}
|
|
32
37
|
function renderItem (obj) {
|
|
@@ -56,20 +61,25 @@ export default function TerminalInfoResource (props) {
|
|
|
56
61
|
</div>
|
|
57
62
|
)
|
|
58
63
|
}
|
|
59
|
-
const data = [
|
|
60
|
-
|
|
64
|
+
const data = []
|
|
65
|
+
if (terminalInfos.includes('cpu')) {
|
|
66
|
+
data.push({
|
|
61
67
|
name: 'cpu',
|
|
62
68
|
percent: parseInt10(cpu)
|
|
63
|
-
}
|
|
64
|
-
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
if (terminalInfos.includes('mem')) {
|
|
72
|
+
data.push({
|
|
65
73
|
name: 'mem',
|
|
66
74
|
...mem
|
|
67
|
-
}
|
|
68
|
-
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
if (terminalInfos.includes('swap')) {
|
|
78
|
+
data.push({
|
|
69
79
|
name: 'swap',
|
|
70
80
|
...swap
|
|
71
|
-
}
|
|
72
|
-
|
|
81
|
+
})
|
|
82
|
+
}
|
|
73
83
|
return (
|
|
74
84
|
<div className='terminal-info-section terminal-info-resource'>
|
|
75
85
|
{
|
package/client/css/basic.styl
CHANGED
package/client/store/common.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import handleError from '../common/error-handler'
|
|
6
|
+
import { Modal } from 'antd'
|
|
6
7
|
import { debounce } from 'lodash-es'
|
|
7
8
|
import postMessage from '../common/post-msg'
|
|
8
9
|
import {
|
|
@@ -15,6 +16,10 @@ import {
|
|
|
15
16
|
} from '../common/constants'
|
|
16
17
|
import * as ls from '../common/safe-local-storage'
|
|
17
18
|
|
|
19
|
+
const { prefix } = window
|
|
20
|
+
const m = prefix('menu')
|
|
21
|
+
const c = prefix('common')
|
|
22
|
+
|
|
18
23
|
export default Store => {
|
|
19
24
|
Store.prototype.storeAssign = function (updates) {
|
|
20
25
|
Object.assign(this, updates)
|
|
@@ -155,4 +160,50 @@ export default Store => {
|
|
|
155
160
|
ls.setItem(dismissDelKeyTipLsKey, 'y')
|
|
156
161
|
window.store.hideDelKeyTip = true
|
|
157
162
|
}
|
|
163
|
+
Store.prototype.beforeExit = function (e) {
|
|
164
|
+
const { confirmBeforeExit } = window.store.config
|
|
165
|
+
if (
|
|
166
|
+
(confirmBeforeExit &&
|
|
167
|
+
!window.confirmExit) ||
|
|
168
|
+
window.store.isTransporting
|
|
169
|
+
) {
|
|
170
|
+
e.returnValue = false
|
|
171
|
+
let mod = null
|
|
172
|
+
mod = Modal.confirm({
|
|
173
|
+
onCancel: () => {
|
|
174
|
+
window.confirmExit = false
|
|
175
|
+
mod.destroy()
|
|
176
|
+
},
|
|
177
|
+
onOk: () => {
|
|
178
|
+
window.confirmExit = true
|
|
179
|
+
window.store[window.exitFunction]()
|
|
180
|
+
},
|
|
181
|
+
title: m('quit'),
|
|
182
|
+
okText: c('ok'),
|
|
183
|
+
cancelText: c('cancel'),
|
|
184
|
+
content: ''
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
Store.prototype.beforeExitApp = function (e, name) {
|
|
189
|
+
let mod = null
|
|
190
|
+
mod = Modal.confirm({
|
|
191
|
+
onCancel: () => {
|
|
192
|
+
window.pre.runGlobalAsync('setCloseAction', 'closeApp')
|
|
193
|
+
mod.destroy()
|
|
194
|
+
},
|
|
195
|
+
onOk: () => {
|
|
196
|
+
window.pre.runGlobalAsync(name)
|
|
197
|
+
},
|
|
198
|
+
title: m('quit'),
|
|
199
|
+
okText: c('ok'),
|
|
200
|
+
cancelText: c('cancel'),
|
|
201
|
+
content: ''
|
|
202
|
+
})
|
|
203
|
+
}
|
|
204
|
+
Store.prototype.setTerminalInfos = function (arr) {
|
|
205
|
+
window.store.setConfig({
|
|
206
|
+
terminalInfos: arr
|
|
207
|
+
})
|
|
208
|
+
}
|
|
158
209
|
}
|
|
@@ -91,30 +91,20 @@ export default Store => {
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
Store.prototype.exit = function () {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
!store.isTransporting &&
|
|
97
|
-
!store.config.confirmBeforeExit
|
|
98
|
-
) {
|
|
99
|
-
return store.doExit()
|
|
100
|
-
}
|
|
101
|
-
store.confirmExit()
|
|
94
|
+
window.exitFunction = 'doExit'
|
|
95
|
+
window.store.doExit()
|
|
102
96
|
}
|
|
103
97
|
|
|
104
98
|
Store.prototype.restart = function () {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
store.confirmExit('doRestart')
|
|
108
|
-
} else {
|
|
109
|
-
store.doRestart()
|
|
110
|
-
}
|
|
99
|
+
window.exitFunction = 'doRestart'
|
|
100
|
+
window.store.doRestart()
|
|
111
101
|
}
|
|
112
102
|
|
|
113
103
|
Store.prototype.doExit = function () {
|
|
114
|
-
window.pre.runGlobalAsync('closeApp')
|
|
104
|
+
window.pre.runGlobalAsync('closeApp', 'exit')
|
|
115
105
|
}
|
|
116
106
|
|
|
117
107
|
Store.prototype.doRestart = function () {
|
|
118
|
-
window.pre.runGlobalAsync('restart')
|
|
108
|
+
window.pre.runGlobalAsync('restart', 'restart')
|
|
119
109
|
}
|
|
120
110
|
}
|