@electerm/electerm-react 2.15.8 → 2.16.6
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/ai/ai-chat.jsx +44 -2
- package/client/components/ai/ai-stop-icon.jsx +13 -0
- package/client/components/ai/ai.styl +10 -0
- package/client/components/bg/css-overwrite.jsx +158 -187
- package/client/components/bg/custom-css.jsx +8 -17
- package/client/components/bookmark-form/bookmark-schema.js +7 -1
- package/client/components/bookmark-form/common/color-picker.jsx +4 -8
- package/client/components/bookmark-form/common/exec-settings-field.jsx +44 -0
- package/client/components/bookmark-form/common/fields.jsx +3 -0
- package/client/components/bookmark-form/config/common-fields.js +1 -0
- package/client/components/bookmark-form/config/local.js +3 -1
- package/client/components/common/animate-text.jsx +22 -23
- package/client/components/common/modal.jsx +2 -0
- package/client/components/common/password.jsx +19 -32
- package/client/components/footer/cmd-history.jsx +154 -0
- package/client/components/footer/cmd-history.styl +73 -0
- package/client/components/footer/footer-entry.jsx +15 -1
- package/client/components/main/main.jsx +2 -3
- package/client/components/quick-commands/quick-commands-select.jsx +1 -4
- package/client/components/rdp/rdp-session.jsx +23 -4
- package/client/components/session/session.styl +1 -3
- package/client/components/setting-panel/terminal-bg-config.jsx +2 -0
- package/client/components/setting-panel/text-bg-modal.jsx +9 -9
- package/client/components/sftp/file-item.jsx +22 -0
- package/client/components/sidebar/history-item.jsx +6 -3
- package/client/components/sidebar/history.jsx +48 -5
- package/client/components/sidebar/sidebar-panel.jsx +0 -13
- package/client/components/sidebar/sidebar.styl +19 -0
- package/client/components/tabs/add-btn-menu.jsx +28 -4
- package/client/components/tabs/add-btn.jsx +1 -1
- package/client/components/tabs/add-btn.styl +8 -0
- package/client/components/terminal/terminal.jsx +28 -11
- package/client/components/terminal/transfer-client-base.js +18 -2
- package/client/components/terminal/trzsz-client.js +2 -1
- package/client/components/terminal/zmodem-client.js +2 -1
- package/client/components/text-editor/edit-with-custom-editor.jsx +49 -0
- package/client/components/text-editor/text-editor-form.jsx +13 -5
- package/client/components/text-editor/text-editor.jsx +20 -1
- package/client/components/vnc/vnc-session.jsx +3 -0
- package/client/components/vnc/vnc.styl +1 -1
- package/client/store/common.js +31 -4
- package/client/store/init-state.js +26 -1
- package/client/store/store.js +1 -1
- package/client/store/watch.js +8 -1
- package/package.json +1 -1
|
@@ -301,6 +301,7 @@ export const sshAuthFields = [
|
|
|
301
301
|
type: 'sshAgent',
|
|
302
302
|
name: 'useSshAgent'
|
|
303
303
|
},
|
|
304
|
+
{ type: 'switch', name: 'isMFA', label: () => e('MFA/OTP'), valuePropName: 'checked' },
|
|
304
305
|
commonFields.runScripts,
|
|
305
306
|
commonFields.description,
|
|
306
307
|
commonFields.setEnv,
|
|
@@ -86,7 +86,9 @@ const localConfig = {
|
|
|
86
86
|
step: 1000
|
|
87
87
|
}
|
|
88
88
|
},
|
|
89
|
-
{ type: 'terminalBackground', name: 'terminalBackground', label: () => e('terminalBackgroundImage') }
|
|
89
|
+
{ type: 'terminalBackground', name: 'terminalBackground', label: () => e('terminalBackgroundImage') },
|
|
90
|
+
// Exec settings - stored as flat properties on bookmark
|
|
91
|
+
{ type: 'execSettings' }
|
|
90
92
|
]
|
|
91
93
|
},
|
|
92
94
|
{
|
|
@@ -2,34 +2,33 @@
|
|
|
2
2
|
* animate text when text change
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import React from 'react'
|
|
5
|
+
import React, { useRef, useEffect } from 'react'
|
|
6
6
|
|
|
7
|
-
export default
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
this.textRef = React.createRef()
|
|
11
|
-
}
|
|
7
|
+
export default function AnimateText ({ children, className }) {
|
|
8
|
+
const textRef = useRef(null)
|
|
9
|
+
const timerRef = useRef(null)
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
const dom =
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const dom = textRef.current
|
|
13
|
+
if (!dom) return
|
|
14
|
+
|
|
15
|
+
dom.className = className || 'animate-text-wrap'
|
|
16
|
+
timerRef.current = setTimeout(() => {
|
|
17
17
|
if (dom) {
|
|
18
|
-
dom.className =
|
|
18
|
+
dom.className = className || 'animate-text-wrap'
|
|
19
19
|
}
|
|
20
20
|
}, 450)
|
|
21
|
-
}
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
return () => {
|
|
23
|
+
if (timerRef.current) {
|
|
24
|
+
clearTimeout(timerRef.current)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}, [className])
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
</div>
|
|
33
|
-
)
|
|
34
|
-
}
|
|
29
|
+
return (
|
|
30
|
+
<div className={className} ref={textRef}>
|
|
31
|
+
{children}
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
35
34
|
}
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
import { useState, useCallback
|
|
1
|
+
import { useState, useCallback } from 'react'
|
|
2
2
|
import {
|
|
3
3
|
Input,
|
|
4
4
|
Tag
|
|
5
5
|
} from 'antd'
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
* Password component that extends Ant Design's Password component
|
|
9
|
-
* with caps lock detection and visual indicator
|
|
10
|
-
*/
|
|
11
|
-
export default forwardRef(function Password (props, ref) {
|
|
7
|
+
export default function Password ({ ref, onKeyDown, onKeyUp, onFocus, onBlur, prefix, ...props }) {
|
|
12
8
|
const [isCapsLockOn, setIsCapsLockOn] = useState(false)
|
|
13
9
|
|
|
14
|
-
// Check caps lock state from keyboard event
|
|
15
10
|
const checkCapsLock = useCallback((event) => {
|
|
16
11
|
if (event.getModifierState) {
|
|
17
12
|
const capsLockState = event.getModifierState('CapsLock')
|
|
@@ -19,37 +14,30 @@ export default forwardRef(function Password (props, ref) {
|
|
|
19
14
|
}
|
|
20
15
|
}, [])
|
|
21
16
|
|
|
22
|
-
// Handle key events to detect caps lock changes
|
|
23
17
|
const handleKeyEvent = useCallback((event) => {
|
|
24
18
|
checkCapsLock(event)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
props.onKeyDown(event)
|
|
19
|
+
if (onKeyDown && event.type === 'keydown') {
|
|
20
|
+
onKeyDown(event)
|
|
28
21
|
}
|
|
29
|
-
if (
|
|
30
|
-
|
|
22
|
+
if (onKeyUp && event.type === 'keyup') {
|
|
23
|
+
onKeyUp(event)
|
|
31
24
|
}
|
|
32
|
-
}, [
|
|
25
|
+
}, [onKeyDown, onKeyUp, checkCapsLock])
|
|
33
26
|
|
|
34
|
-
// Handle focus event to check initial caps lock state
|
|
35
27
|
const handleFocus = useCallback((event) => {
|
|
36
28
|
checkCapsLock(event)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
props.onFocus(event)
|
|
29
|
+
if (onFocus) {
|
|
30
|
+
onFocus(event)
|
|
40
31
|
}
|
|
41
|
-
}, [
|
|
32
|
+
}, [onFocus, checkCapsLock])
|
|
42
33
|
|
|
43
|
-
// Handle blur event to reset caps lock state
|
|
44
34
|
const handleBlur = useCallback((event) => {
|
|
45
35
|
setIsCapsLockOn(false)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
props.onBlur(event)
|
|
36
|
+
if (onBlur) {
|
|
37
|
+
onBlur(event)
|
|
49
38
|
}
|
|
50
|
-
}, [
|
|
39
|
+
}, [onBlur])
|
|
51
40
|
|
|
52
|
-
// Show caps lock indicator inside prefix to avoid remounting the input wrapper
|
|
53
41
|
let capsPrefix = null
|
|
54
42
|
if (isCapsLockOn) {
|
|
55
43
|
capsPrefix = (
|
|
@@ -57,13 +45,12 @@ export default forwardRef(function Password (props, ref) {
|
|
|
57
45
|
)
|
|
58
46
|
}
|
|
59
47
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
prefix = (
|
|
48
|
+
let mergedPrefix = capsPrefix
|
|
49
|
+
if (prefix) {
|
|
50
|
+
mergedPrefix = (
|
|
64
51
|
<>
|
|
65
52
|
{capsPrefix}
|
|
66
|
-
{
|
|
53
|
+
{prefix}
|
|
67
54
|
</>
|
|
68
55
|
)
|
|
69
56
|
}
|
|
@@ -72,11 +59,11 @@ export default forwardRef(function Password (props, ref) {
|
|
|
72
59
|
<Input.Password
|
|
73
60
|
{...props}
|
|
74
61
|
ref={ref}
|
|
75
|
-
prefix={
|
|
62
|
+
prefix={mergedPrefix}
|
|
76
63
|
onKeyDown={handleKeyEvent}
|
|
77
64
|
onKeyUp={handleKeyEvent}
|
|
78
65
|
onFocus={handleFocus}
|
|
79
66
|
onBlur={handleBlur}
|
|
80
67
|
/>
|
|
81
68
|
)
|
|
82
|
-
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cmd history trigger button with popover
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useState, useEffect } from 'react'
|
|
6
|
+
import { Button, Empty, Popover, Switch } from 'antd'
|
|
7
|
+
import { auto } from 'manate/react'
|
|
8
|
+
import { copy } from '../../common/clipboard'
|
|
9
|
+
import { HistoryOutlined, DeleteOutlined, CopyOutlined, UnorderedListOutlined } from '@ant-design/icons'
|
|
10
|
+
import InputAutoFocus from '../common/input-auto-focus'
|
|
11
|
+
import { getItemJSON, setItemJSON } from '../../common/safe-local-storage'
|
|
12
|
+
import './cmd-history.styl'
|
|
13
|
+
|
|
14
|
+
const e = window.translate
|
|
15
|
+
const SORT_BY_FREQ_KEY = 'electerm-cmd-history-sort-by-frequency'
|
|
16
|
+
|
|
17
|
+
export default auto(function CmdHistory (props) {
|
|
18
|
+
const [keyword, setKeyword] = useState('')
|
|
19
|
+
const [sortByFrequency, setSortByFrequency] = useState(() => {
|
|
20
|
+
return getItemJSON(SORT_BY_FREQ_KEY, false)
|
|
21
|
+
})
|
|
22
|
+
const { terminalCommandHistory } = props.store
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
setItemJSON(SORT_BY_FREQ_KEY, sortByFrequency)
|
|
26
|
+
}, [sortByFrequency])
|
|
27
|
+
|
|
28
|
+
function handleRunCommand (cmd) {
|
|
29
|
+
window.store.runCmdFromHistory(cmd)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function handleDeleteCommand (cmd, ev) {
|
|
33
|
+
ev.stopPropagation()
|
|
34
|
+
terminalCommandHistory.delete(cmd)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function handleCopyCommand (cmd, ev) {
|
|
38
|
+
ev.stopPropagation()
|
|
39
|
+
copy(cmd)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function handleClearAll () {
|
|
43
|
+
window.store.clearAllCmdHistory()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function filterArray (array, keyword) {
|
|
47
|
+
if (!keyword) {
|
|
48
|
+
return array
|
|
49
|
+
}
|
|
50
|
+
return array.filter(item => item.cmd.toLowerCase().includes(keyword.toLowerCase()))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function handleChange (e) {
|
|
54
|
+
setKeyword(e.target.value)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const historyArray = Array.from(terminalCommandHistory || [])
|
|
58
|
+
.map(([cmd, info]) => ({ cmd, ...info }))
|
|
59
|
+
.reverse()
|
|
60
|
+
|
|
61
|
+
let filtered = filterArray(historyArray, keyword)
|
|
62
|
+
|
|
63
|
+
if (sortByFrequency) {
|
|
64
|
+
filtered = filtered.sort((a, b) => b.count - a.count)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const handleSortByFrequencyChange = (checked) => {
|
|
68
|
+
setSortByFrequency(checked)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const renderList = () => {
|
|
72
|
+
if (filtered.length === 0) {
|
|
73
|
+
return (
|
|
74
|
+
<Empty
|
|
75
|
+
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
76
|
+
description={e('noData')}
|
|
77
|
+
/>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
return filtered.map((item, index) => (
|
|
81
|
+
<div
|
|
82
|
+
key={index}
|
|
83
|
+
className='cmd-history-item'
|
|
84
|
+
onClick={() => handleRunCommand(item.cmd)}
|
|
85
|
+
>
|
|
86
|
+
<span className='cmd-history-item-text' title={item.cmd}>{item.cmd}</span>
|
|
87
|
+
<div className='cmd-history-item-actions'>
|
|
88
|
+
<span className='cmd-history-item-count' title={e('count') + ': ' + item.count}>
|
|
89
|
+
{item.count}
|
|
90
|
+
</span>
|
|
91
|
+
<Button
|
|
92
|
+
type='text'
|
|
93
|
+
size='small'
|
|
94
|
+
icon={<CopyOutlined />}
|
|
95
|
+
className='cmd-history-item-copy'
|
|
96
|
+
onClick={(ev) => handleCopyCommand(item.cmd, ev)}
|
|
97
|
+
/>
|
|
98
|
+
<Button
|
|
99
|
+
type='text'
|
|
100
|
+
size='small'
|
|
101
|
+
icon={<DeleteOutlined />}
|
|
102
|
+
className='cmd-history-item-delete'
|
|
103
|
+
onClick={(ev) => handleDeleteCommand(item.cmd, ev)}
|
|
104
|
+
/>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const content = (
|
|
111
|
+
<div className='cmd-history-popover-content pd2'>
|
|
112
|
+
<div className='cmd-history-search pd2b'>
|
|
113
|
+
<InputAutoFocus
|
|
114
|
+
value={keyword}
|
|
115
|
+
onChange={handleChange}
|
|
116
|
+
placeholder={e('search')}
|
|
117
|
+
className='cmd-history-search-input'
|
|
118
|
+
allowClear
|
|
119
|
+
/>
|
|
120
|
+
</div>
|
|
121
|
+
<div className='cmd-history-header pd2b'>
|
|
122
|
+
<Switch
|
|
123
|
+
checkedChildren={e('sortByFrequency')}
|
|
124
|
+
unCheckedChildren={e('sortByFrequency')}
|
|
125
|
+
checked={sortByFrequency}
|
|
126
|
+
onChange={handleSortByFrequencyChange}
|
|
127
|
+
size='small'
|
|
128
|
+
/>
|
|
129
|
+
<UnorderedListOutlined
|
|
130
|
+
className='cmd-history-clear-icon pointer clear-ai-icon icon-hover'
|
|
131
|
+
title={e('clear')}
|
|
132
|
+
onClick={handleClearAll}
|
|
133
|
+
/>
|
|
134
|
+
</div>
|
|
135
|
+
<div className='cmd-history-list'>
|
|
136
|
+
{renderList()}
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<Popover
|
|
143
|
+
content={content}
|
|
144
|
+
trigger='click'
|
|
145
|
+
placement='topLeft'
|
|
146
|
+
>
|
|
147
|
+
<Button
|
|
148
|
+
size='small'
|
|
149
|
+
type='text'
|
|
150
|
+
icon={<HistoryOutlined />}
|
|
151
|
+
/>
|
|
152
|
+
</Popover>
|
|
153
|
+
)
|
|
154
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
.cmd-history-popover-content
|
|
2
|
+
max-height 300px
|
|
3
|
+
width 300px
|
|
4
|
+
overflow hidden
|
|
5
|
+
display flex
|
|
6
|
+
flex-direction column
|
|
7
|
+
|
|
8
|
+
.cmd-history-search
|
|
9
|
+
flex-shrink 0
|
|
10
|
+
position sticky
|
|
11
|
+
top 0
|
|
12
|
+
|
|
13
|
+
.cmd-history-header
|
|
14
|
+
flex-shrink 0
|
|
15
|
+
display flex
|
|
16
|
+
align-items center
|
|
17
|
+
justify-content space-between
|
|
18
|
+
padding-bottom 8px
|
|
19
|
+
|
|
20
|
+
.cmd-history-clear-icon
|
|
21
|
+
font-size 14px
|
|
22
|
+
padding 4px
|
|
23
|
+
cursor pointer
|
|
24
|
+
&:hover
|
|
25
|
+
color red
|
|
26
|
+
|
|
27
|
+
.cmd-history-list
|
|
28
|
+
overflow-x hidden
|
|
29
|
+
overflow-y auto
|
|
30
|
+
flex 1
|
|
31
|
+
min-height 0
|
|
32
|
+
|
|
33
|
+
.cmd-history-item
|
|
34
|
+
display flex
|
|
35
|
+
align-items center
|
|
36
|
+
justify-content space-between
|
|
37
|
+
padding 4px 8px
|
|
38
|
+
cursor pointer
|
|
39
|
+
&:hover
|
|
40
|
+
background var(--main-lighter)
|
|
41
|
+
|
|
42
|
+
.cmd-history-item-text
|
|
43
|
+
overflow hidden
|
|
44
|
+
text-overflow ellipsis
|
|
45
|
+
white-space nowrap
|
|
46
|
+
flex 1
|
|
47
|
+
min-width 0
|
|
48
|
+
|
|
49
|
+
.cmd-history-item-count
|
|
50
|
+
font-size 12px
|
|
51
|
+
color var(--text-color-secondary)
|
|
52
|
+
padding 0 4px
|
|
53
|
+
flex-shrink 0
|
|
54
|
+
|
|
55
|
+
.cmd-history-item-actions
|
|
56
|
+
display none
|
|
57
|
+
flex-shrink 0
|
|
58
|
+
gap 4px
|
|
59
|
+
align-items center
|
|
60
|
+
.cmd-history-item:hover &
|
|
61
|
+
display flex
|
|
62
|
+
|
|
63
|
+
.cmd-history-item-delete
|
|
64
|
+
cursor pointer
|
|
65
|
+
padding 2px 4px
|
|
66
|
+
&:hover
|
|
67
|
+
color red
|
|
68
|
+
|
|
69
|
+
.cmd-history-item-copy
|
|
70
|
+
cursor pointer
|
|
71
|
+
padding 2px 4px
|
|
72
|
+
&:hover
|
|
73
|
+
color blue
|
|
@@ -10,6 +10,7 @@ import encodes from '../bookmark-form/common/encodes'
|
|
|
10
10
|
import { refs } from '../common/ref'
|
|
11
11
|
import Qm from '../quick-commands/quick-commands-select'
|
|
12
12
|
import AIIcon from '../icons/ai-icon'
|
|
13
|
+
import CmdHistory from './cmd-history'
|
|
13
14
|
|
|
14
15
|
const {
|
|
15
16
|
Option
|
|
@@ -131,7 +132,19 @@ export default auto(function FooterEntry (props) {
|
|
|
131
132
|
)
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
|
|
135
|
+
function renderCmdHistory () {
|
|
136
|
+
return (
|
|
137
|
+
<div className='terminal-footer-unit terminal-footer-history'>
|
|
138
|
+
<CmdHistory store={props.store} />
|
|
139
|
+
</div>
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const {
|
|
144
|
+
leftSidebarWidth,
|
|
145
|
+
openedSideBar,
|
|
146
|
+
inActiveTerminal
|
|
147
|
+
} = props.store
|
|
135
148
|
const w = 43 + leftSidebarWidth
|
|
136
149
|
const sideProps = openedSideBar
|
|
137
150
|
? {
|
|
@@ -154,6 +167,7 @@ export default auto(function FooterEntry (props) {
|
|
|
154
167
|
<div {...sideProps}>
|
|
155
168
|
<div className='terminal-footer-flex'>
|
|
156
169
|
{renderAIIcon()}
|
|
170
|
+
{renderCmdHistory()}
|
|
157
171
|
{renderQuickCommands()}
|
|
158
172
|
{renderBatchInputs()}
|
|
159
173
|
{renderEncodingInfo()}
|
|
@@ -86,7 +86,6 @@ export default auto(function Index (props) {
|
|
|
86
86
|
pinned,
|
|
87
87
|
isSecondInstance,
|
|
88
88
|
pinnedQuickCommandBar,
|
|
89
|
-
wsInited,
|
|
90
89
|
installSrc,
|
|
91
90
|
fileTransfers,
|
|
92
91
|
uiThemeConfig,
|
|
@@ -248,14 +247,14 @@ export default auto(function Index (props) {
|
|
|
248
247
|
<ShortcutControl config={config} />
|
|
249
248
|
<CssOverwrite
|
|
250
249
|
{...confsCss}
|
|
251
|
-
|
|
250
|
+
configLoaded={configLoaded}
|
|
252
251
|
/>
|
|
253
252
|
<Opacity opacity={config.opacity} />
|
|
254
253
|
<TerminalInteractive />
|
|
255
254
|
<UiTheme
|
|
256
255
|
{...themeProps}
|
|
257
256
|
/>
|
|
258
|
-
<CustomCss customCss={config.customCss} />
|
|
257
|
+
<CustomCss customCss={config.customCss} configLoaded={configLoaded} />
|
|
259
258
|
<TextEditor />
|
|
260
259
|
<UpdateCheck
|
|
261
260
|
skipVersion={config.skipVersion}
|
|
@@ -6,8 +6,6 @@ import { PureComponent } from 'react'
|
|
|
6
6
|
import { Button } from 'antd'
|
|
7
7
|
import './qm.styl'
|
|
8
8
|
|
|
9
|
-
const e = window.translate
|
|
10
|
-
|
|
11
9
|
export default class QuickCommandsFooter extends PureComponent {
|
|
12
10
|
componentWillUnmount () {
|
|
13
11
|
clearTimeout(this.timer)
|
|
@@ -37,8 +35,7 @@ export default class QuickCommandsFooter extends PureComponent {
|
|
|
37
35
|
size='small'
|
|
38
36
|
type='text'
|
|
39
37
|
>
|
|
40
|
-
|
|
41
|
-
<span className='l500'>Q</span>
|
|
38
|
+
Q
|
|
42
39
|
</Button>
|
|
43
40
|
</div>
|
|
44
41
|
)
|
|
@@ -339,10 +339,29 @@ export default class RdpSession extends PureComponent {
|
|
|
339
339
|
if (!this.session) return
|
|
340
340
|
try {
|
|
341
341
|
const rect = canvas.getBoundingClientRect()
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
342
|
+
const { scaleViewport } = this.state
|
|
343
|
+
let scaleX = canvas.width / rect.width
|
|
344
|
+
let scaleY = canvas.height / rect.height
|
|
345
|
+
let offsetX = 0
|
|
346
|
+
let offsetY = 0
|
|
347
|
+
if (scaleViewport) {
|
|
348
|
+
const containerRatio = rect.width / rect.height
|
|
349
|
+
const canvasRatio = canvas.width / canvas.height
|
|
350
|
+
let renderWidth, renderHeight
|
|
351
|
+
if (containerRatio > canvasRatio) {
|
|
352
|
+
renderHeight = rect.height
|
|
353
|
+
renderWidth = rect.height * canvasRatio
|
|
354
|
+
offsetX = (rect.width - renderWidth) / 2
|
|
355
|
+
} else {
|
|
356
|
+
renderWidth = rect.width
|
|
357
|
+
renderHeight = rect.width / canvasRatio
|
|
358
|
+
offsetY = (rect.height - renderHeight) / 2
|
|
359
|
+
}
|
|
360
|
+
scaleX = canvas.width / renderWidth
|
|
361
|
+
scaleY = canvas.height / renderHeight
|
|
362
|
+
}
|
|
363
|
+
const x = Math.round((e.clientX - rect.left - offsetX) * scaleX)
|
|
364
|
+
const y = Math.round((e.clientY - rect.top - offsetY) * scaleY)
|
|
346
365
|
const event = window.ironRdp.DeviceEvent.mouseMove(x, y)
|
|
347
366
|
const tx = new window.ironRdp.InputTransaction()
|
|
348
367
|
tx.addEvent(event)
|
|
@@ -3,15 +3,13 @@ import {
|
|
|
3
3
|
Input,
|
|
4
4
|
InputNumber,
|
|
5
5
|
Space,
|
|
6
|
-
Typography,
|
|
7
6
|
Select,
|
|
8
|
-
Button
|
|
7
|
+
Button,
|
|
8
|
+
Modal
|
|
9
9
|
} from 'antd'
|
|
10
|
-
import Modal from '../common/modal'
|
|
11
10
|
import { ColorPicker } from '../bookmark-form/common/color-picker.jsx'
|
|
12
11
|
|
|
13
12
|
const { TextArea } = Input
|
|
14
|
-
const { Title } = Typography
|
|
15
13
|
const e = window.translate
|
|
16
14
|
|
|
17
15
|
export default function TextBgModal ({
|
|
@@ -68,9 +66,9 @@ export default function TextBgModal ({
|
|
|
68
66
|
footer={footer}
|
|
69
67
|
>
|
|
70
68
|
<div className='pd1'>
|
|
71
|
-
<Space
|
|
69
|
+
<Space orientation='vertical' size='large' className='width-100'>
|
|
72
70
|
<div>
|
|
73
|
-
<
|
|
71
|
+
<b>{e('text')}</b>
|
|
74
72
|
<TextArea
|
|
75
73
|
value={text}
|
|
76
74
|
onChange={(e) => setText(e.target.value)}
|
|
@@ -81,7 +79,7 @@ export default function TextBgModal ({
|
|
|
81
79
|
</div>
|
|
82
80
|
|
|
83
81
|
<div>
|
|
84
|
-
<
|
|
82
|
+
<b>{e('fontSize')}</b>
|
|
85
83
|
<InputNumber
|
|
86
84
|
value={fontSize}
|
|
87
85
|
onChange={setFontSize}
|
|
@@ -93,7 +91,7 @@ export default function TextBgModal ({
|
|
|
93
91
|
</div>
|
|
94
92
|
|
|
95
93
|
<div>
|
|
96
|
-
<
|
|
94
|
+
<b>{e('textColor')}</b>
|
|
97
95
|
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
98
96
|
<ColorPicker
|
|
99
97
|
value={color}
|
|
@@ -109,7 +107,7 @@ export default function TextBgModal ({
|
|
|
109
107
|
</div>
|
|
110
108
|
|
|
111
109
|
<div>
|
|
112
|
-
<
|
|
110
|
+
<b>{e('fontFamily')}</b>
|
|
113
111
|
<Select
|
|
114
112
|
value={fontFamily}
|
|
115
113
|
onChange={setFontFamily}
|
|
@@ -140,3 +138,5 @@ export default function TextBgModal ({
|
|
|
140
138
|
</Modal>
|
|
141
139
|
)
|
|
142
140
|
}
|
|
141
|
+
|
|
142
|
+
TextBgModal.displayName = 'TextBgModal'
|
|
@@ -651,6 +651,28 @@ export default class FileSection extends React.Component {
|
|
|
651
651
|
this.watchFile(tempPath)
|
|
652
652
|
}
|
|
653
653
|
|
|
654
|
+
editWithCustomEditor = async (text, editorCommand) => {
|
|
655
|
+
const {
|
|
656
|
+
path,
|
|
657
|
+
name,
|
|
658
|
+
type
|
|
659
|
+
} = this.state.file
|
|
660
|
+
let tempPath = ''
|
|
661
|
+
if (type === typeMap.local) {
|
|
662
|
+
tempPath = window.pre.resolve(path, name)
|
|
663
|
+
} else {
|
|
664
|
+
const id = generate()
|
|
665
|
+
tempPath = window.pre.resolve(
|
|
666
|
+
window.pre.tempDir, `electerm-temp-${id}-${name}`
|
|
667
|
+
)
|
|
668
|
+
await fs.writeFile(tempPath, text)
|
|
669
|
+
}
|
|
670
|
+
this.watchingFile = tempPath
|
|
671
|
+
window.pre.runGlobalAsync('watchFile', tempPath)
|
|
672
|
+
await window.pre.runGlobalAsync('openFileWithEditor', tempPath, editorCommand)
|
|
673
|
+
window.pre.ipcOnEvent('file-change', this.onFileChange)
|
|
674
|
+
}
|
|
675
|
+
|
|
654
676
|
onFileChange = (e, text) => {
|
|
655
677
|
this.editor.editWithSystemEditorDone({
|
|
656
678
|
id: this.id,
|
|
@@ -6,8 +6,7 @@ import { refsStatic } from '../common/ref'
|
|
|
6
6
|
export default function HistoryItem (props) {
|
|
7
7
|
const { store } = window
|
|
8
8
|
const {
|
|
9
|
-
item
|
|
10
|
-
index
|
|
9
|
+
item
|
|
11
10
|
} = props
|
|
12
11
|
const timeoutRef = useRef(null)
|
|
13
12
|
|
|
@@ -30,7 +29,11 @@ export default function HistoryItem (props) {
|
|
|
30
29
|
|
|
31
30
|
function handleDelete (e) {
|
|
32
31
|
e.stopPropagation()
|
|
33
|
-
|
|
32
|
+
const { id } = item
|
|
33
|
+
const i = store.history.findIndex((i) => i.id === id)
|
|
34
|
+
if (i !== -1) {
|
|
35
|
+
store.history.splice(i, 1)
|
|
36
|
+
}
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
function handleBookmark (e) {
|