@electerm/electerm-react 3.1.26 → 3.2.0
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/db.js +4 -2
- package/client/components/ai/ai-history.jsx +4 -4
- package/client/components/bookmark-form/common/bookmark-select.jsx +18 -2
- package/client/components/bookmark-form/common/connection-hopping-form.jsx +153 -0
- package/client/components/bookmark-form/common/connection-hopping.jsx +136 -129
- package/client/components/quick-commands/qm.styl +0 -2
- package/client/components/quick-commands/quick-commands-list-form.jsx +1 -1
- package/client/components/setting-panel/hotkey.jsx +9 -1
- package/client/components/setting-panel/list.jsx +0 -1
- package/client/components/setting-panel/list.styl +4 -0
- package/client/components/setting-panel/setting-modal.jsx +53 -47
- package/client/components/shortcuts/shortcut-editor.jsx +4 -2
- package/client/components/sidebar/history.jsx +1 -0
- package/client/components/terminal/attach-addon-custom.js +86 -0
- package/client/components/terminal/cmd-item.jsx +13 -3
- package/client/components/terminal/drop-file-modal.jsx +57 -0
- package/client/components/terminal/terminal-command-dropdown.jsx +91 -13
- package/client/components/terminal/terminal.jsx +103 -5
- package/client/components/terminal/terminal.styl +9 -0
- package/client/components/tree-list/tree-list-item.jsx +0 -1
- package/client/components/vnc/vnc-session.jsx +2 -0
- package/client/components/widgets/widget-control.jsx +3 -0
- package/client/components/widgets/widget-instance.jsx +26 -7
- package/client/css/includes/box.styl +3 -0
- package/client/store/init-state.js +2 -1
- package/client/store/load-data.js +3 -1
- package/client/store/mcp-handler.js +18 -0
- package/client/store/widgets.js +54 -0
- package/package.json +1 -1
|
@@ -4,18 +4,22 @@
|
|
|
4
4
|
|
|
5
5
|
import { auto } from 'manate/react'
|
|
6
6
|
import { pick } from 'lodash-es'
|
|
7
|
-
import { Tabs } from 'antd'
|
|
7
|
+
import { Tabs, Spin } from 'antd'
|
|
8
|
+
import { lazy, Suspense } from 'react'
|
|
8
9
|
import SettingModal from './setting-wrap'
|
|
9
10
|
import {
|
|
10
11
|
settingMap,
|
|
11
12
|
modals
|
|
12
13
|
} from '../../common/constants'
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
|
|
15
|
+
const TabBookmarks = lazy(() => import('./tab-bookmarks'))
|
|
16
|
+
const TabQuickCommands = lazy(() => import('./tab-quick-commands'))
|
|
17
|
+
const TabSettings = lazy(() => import('./tab-settings'))
|
|
18
|
+
const TabThemes = lazy(() => import('./tab-themes'))
|
|
19
|
+
const TabProfiles = lazy(() => import('./tab-profiles'))
|
|
20
|
+
const TabWidgets = lazy(() => import('./tab-widgets'))
|
|
21
|
+
|
|
22
|
+
const Loading = () => <div style={{ padding: 20, textAlign: 'center' }}><Spin /></div>
|
|
19
23
|
|
|
20
24
|
const e = window.translate
|
|
21
25
|
|
|
@@ -115,46 +119,48 @@ export default auto(function SettingModalWrap (props) {
|
|
|
115
119
|
<Tabs
|
|
116
120
|
{...tabsProps}
|
|
117
121
|
/>
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
122
|
+
<Suspense fallback={<Loading />}>
|
|
123
|
+
<TabQuickCommands
|
|
124
|
+
listProps={props0}
|
|
125
|
+
settingItem={settingItem}
|
|
126
|
+
formProps={formProps}
|
|
127
|
+
store={store}
|
|
128
|
+
settingTab={settingTab}
|
|
129
|
+
/>
|
|
130
|
+
<TabBookmarks
|
|
131
|
+
treeProps={treeProps}
|
|
132
|
+
settingItem={settingItem}
|
|
133
|
+
formProps={formProps}
|
|
134
|
+
settingTab={settingTab}
|
|
135
|
+
/>
|
|
136
|
+
<TabSettings
|
|
137
|
+
listProps={props0}
|
|
138
|
+
settingItem={settingItem}
|
|
139
|
+
settingTab={settingTab}
|
|
140
|
+
store={store}
|
|
141
|
+
/>
|
|
142
|
+
<TabThemes
|
|
143
|
+
listProps={props0}
|
|
144
|
+
settingItem={settingItem}
|
|
145
|
+
formProps={formProps}
|
|
146
|
+
store={store}
|
|
147
|
+
settingTab={settingTab}
|
|
148
|
+
/>
|
|
149
|
+
<TabProfiles
|
|
150
|
+
listProps={props0}
|
|
151
|
+
settingItem={settingItem}
|
|
152
|
+
formProps={formProps}
|
|
153
|
+
store={store}
|
|
154
|
+
settingTab={settingTab}
|
|
155
|
+
/>
|
|
156
|
+
<TabWidgets
|
|
157
|
+
listProps={props0}
|
|
158
|
+
settingItem={settingItem}
|
|
159
|
+
formProps={formProps}
|
|
160
|
+
store={store}
|
|
161
|
+
settingTab={settingTab}
|
|
162
|
+
/>
|
|
163
|
+
</Suspense>
|
|
158
164
|
</>
|
|
159
165
|
)
|
|
160
166
|
}
|
|
@@ -135,11 +135,13 @@ export default class ShortcutEdit extends PureComponent {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
renderClear () {
|
|
138
|
-
|
|
138
|
+
const { renderClear, handleClear, data } = this.props
|
|
139
|
+
const hasShortcut = data && data.shortcut
|
|
140
|
+
if (renderClear && hasShortcut && handleClear) {
|
|
139
141
|
return (
|
|
140
142
|
<CloseOutlined
|
|
141
143
|
className='pointer mg1l'
|
|
142
|
-
onClick={
|
|
144
|
+
onClick={handleClear}
|
|
143
145
|
/>
|
|
144
146
|
)
|
|
145
147
|
}
|
|
@@ -8,6 +8,7 @@ import { Switch } from 'antd'
|
|
|
8
8
|
import { UnorderedListOutlined } from '@ant-design/icons'
|
|
9
9
|
import HistoryItem from './history-item'
|
|
10
10
|
import { getItemJSON, setItemJSON } from '../../common/safe-local-storage.js'
|
|
11
|
+
import '../setting-panel/list.styl'
|
|
11
12
|
|
|
12
13
|
const SORT_BY_FREQ_KEY = 'electerm-history-sort-by-frequency'
|
|
13
14
|
|
|
@@ -19,6 +19,10 @@ export default class AttachAddonCustom {
|
|
|
19
19
|
this._lastInputTime = Date.now()
|
|
20
20
|
this._keepaliveTimer = null
|
|
21
21
|
this._keepaliveInterval = 3000
|
|
22
|
+
this._lastOutputLine = ''
|
|
23
|
+
this._passwordPromptDetected = false
|
|
24
|
+
this._pendingEchoCheck = null
|
|
25
|
+
this._echoCheckTimer = null
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
_initBase = async () => {
|
|
@@ -101,6 +105,52 @@ export default class AttachAddonCustom {
|
|
|
101
105
|
this.writeToTerminal(ev.data)
|
|
102
106
|
}
|
|
103
107
|
|
|
108
|
+
static passwordPromptPatterns = [
|
|
109
|
+
/password\s*[:\]>]\s*$/i,
|
|
110
|
+
/\[sudo\]\s*password\s+for\s+\S+\s*:\s*$/i,
|
|
111
|
+
/enter\s+passphrase/i,
|
|
112
|
+
/enter\s+password/i,
|
|
113
|
+
/密码[::]\s*$/,
|
|
114
|
+
/パスワード[::]\s*$/,
|
|
115
|
+
/mot de passe\s*[:\]]\s*$/i,
|
|
116
|
+
/passwort[:\]]\s*$/i,
|
|
117
|
+
/contraseña[:\]]\s*$/i
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
_checkPasswordPrompt = (str) => {
|
|
121
|
+
// Extract last non-empty line from the output
|
|
122
|
+
const lines = str.split(/\r?\n|\r/)
|
|
123
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
124
|
+
const line = lines[i].trim()
|
|
125
|
+
if (line) {
|
|
126
|
+
this._lastOutputLine = line
|
|
127
|
+
break
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return AttachAddonCustom.passwordPromptPatterns.some(
|
|
131
|
+
p => p.test(this._lastOutputLine)
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_onEchoCheckTimeout = () => {
|
|
136
|
+
// No echo received within timeout → confirms password mode
|
|
137
|
+
this._pendingEchoCheck = null
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
_handleEchoDetection = (str) => {
|
|
141
|
+
if (this._pendingEchoCheck) {
|
|
142
|
+
// Server sent data back while we were waiting → echo is ON → not password
|
|
143
|
+
if (str.includes(this._pendingEchoCheck.char)) {
|
|
144
|
+
this._passwordPromptDetected = false
|
|
145
|
+
clearTimeout(this._echoCheckTimer)
|
|
146
|
+
this._pendingEchoCheck = null
|
|
147
|
+
this._echoCheckTimer = null
|
|
148
|
+
// Cancel the password dropdown if it was shown
|
|
149
|
+
this.term?.parent?.onPasswordPromptCancelled?.()
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
104
154
|
checkForShellIntegration = (str) => {
|
|
105
155
|
const ESC = String.fromCharCode(27)
|
|
106
156
|
return str.includes(ESC + ']633;')
|
|
@@ -152,6 +202,26 @@ export default class AttachAddonCustom {
|
|
|
152
202
|
return
|
|
153
203
|
}
|
|
154
204
|
|
|
205
|
+
// Password prompt detection on output
|
|
206
|
+
let str = data
|
|
207
|
+
if (typeof data !== 'string') {
|
|
208
|
+
try {
|
|
209
|
+
str = this.decoder.decode(
|
|
210
|
+
data instanceof ArrayBuffer ? data : new Uint8Array(data)
|
|
211
|
+
)
|
|
212
|
+
} catch (e) {
|
|
213
|
+
str = ''
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
this._handleEchoDetection(str)
|
|
217
|
+
if (this._checkPasswordPrompt(str) && !this._passwordPromptDetected) {
|
|
218
|
+
this._passwordPromptDetected = true
|
|
219
|
+
// Show password dropdown immediately after terminal renders the prompt
|
|
220
|
+
setTimeout(() => {
|
|
221
|
+
this.term?.parent?.onPasswordPromptDetected?.()
|
|
222
|
+
}, 100)
|
|
223
|
+
}
|
|
224
|
+
|
|
155
225
|
if (typeof data === 'string') {
|
|
156
226
|
return term.write(data)
|
|
157
227
|
}
|
|
@@ -171,6 +241,20 @@ export default class AttachAddonCustom {
|
|
|
171
241
|
|
|
172
242
|
sendToServer = (data) => {
|
|
173
243
|
this._lastInputTime = Date.now()
|
|
244
|
+
// Start echo detection when password prompt is suspected
|
|
245
|
+
if (this._passwordPromptDetected && !this._pendingEchoCheck && data !== '\r' && data !== '\n') {
|
|
246
|
+
this._pendingEchoCheck = { char: data, time: Date.now() }
|
|
247
|
+
clearTimeout(this._echoCheckTimer)
|
|
248
|
+
this._echoCheckTimer = setTimeout(this._onEchoCheckTimeout, 200)
|
|
249
|
+
}
|
|
250
|
+
// Reset password state on Enter
|
|
251
|
+
if (data === '\r' || data === '\n') {
|
|
252
|
+
this._passwordPromptDetected = false
|
|
253
|
+
this._lastOutputLine = ''
|
|
254
|
+
this._pendingEchoCheck = null
|
|
255
|
+
clearTimeout(this._echoCheckTimer)
|
|
256
|
+
this._echoCheckTimer = null
|
|
257
|
+
}
|
|
174
258
|
this._sendData(data)
|
|
175
259
|
}
|
|
176
260
|
|
|
@@ -228,6 +312,8 @@ export default class AttachAddonCustom {
|
|
|
228
312
|
|
|
229
313
|
dispose = () => {
|
|
230
314
|
this._stopKeepalive()
|
|
315
|
+
clearTimeout(this._echoCheckTimer)
|
|
316
|
+
this._echoCheckTimer = null
|
|
231
317
|
this.term = null
|
|
232
318
|
this._disposables.forEach(d => d.dispose())
|
|
233
319
|
this._disposables.length = 0
|
|
@@ -11,11 +11,21 @@ const SuggestionItem = ({ item, onSelect, onDelete }) => {
|
|
|
11
11
|
onDelete(item)
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
const isPassword = item.type === 'PW'
|
|
15
|
+
const displayText = isPassword
|
|
16
|
+
? '••••••••'
|
|
17
|
+
: item.command
|
|
18
|
+
|
|
14
19
|
return (
|
|
15
|
-
<div className='suggestion-item'>
|
|
16
|
-
<span className='suggestion-command'
|
|
17
|
-
{
|
|
20
|
+
<div className='suggestion-item' onClick={handleClick}>
|
|
21
|
+
<span className='suggestion-command'>
|
|
22
|
+
{displayText}
|
|
18
23
|
</span>
|
|
24
|
+
{item.hint && (
|
|
25
|
+
<span className='suggestion-hint'>
|
|
26
|
+
{item.hint}
|
|
27
|
+
</span>
|
|
28
|
+
)}
|
|
19
29
|
<span className='suggestion-type'>
|
|
20
30
|
{item.type}
|
|
21
31
|
</span>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Component } from 'react'
|
|
2
|
+
import Modal from '../common/modal'
|
|
3
|
+
|
|
4
|
+
const e = window.translate
|
|
5
|
+
|
|
6
|
+
export class DropFileModal extends Component {
|
|
7
|
+
render () {
|
|
8
|
+
const {
|
|
9
|
+
visible,
|
|
10
|
+
files,
|
|
11
|
+
onSelect,
|
|
12
|
+
onCancel
|
|
13
|
+
} = this.props
|
|
14
|
+
|
|
15
|
+
if (!visible) {
|
|
16
|
+
return null
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<Modal
|
|
21
|
+
title='?'
|
|
22
|
+
open={visible}
|
|
23
|
+
onCancel={onCancel}
|
|
24
|
+
footer={
|
|
25
|
+
<div className='custom-modal-footer-buttons'>
|
|
26
|
+
<button
|
|
27
|
+
type='button'
|
|
28
|
+
className='custom-modal-ok-btn'
|
|
29
|
+
onClick={() => onSelect('trzUpload')}
|
|
30
|
+
>
|
|
31
|
+
trz
|
|
32
|
+
</button>
|
|
33
|
+
<button
|
|
34
|
+
type='button'
|
|
35
|
+
className='custom-modal-cancel-btn'
|
|
36
|
+
onClick={() => onSelect('rzUpload')}
|
|
37
|
+
>
|
|
38
|
+
rz
|
|
39
|
+
</button>
|
|
40
|
+
<button
|
|
41
|
+
type='button'
|
|
42
|
+
className='custom-modal-cancel-btn'
|
|
43
|
+
onClick={() => onSelect('inputPath')}
|
|
44
|
+
>
|
|
45
|
+
{e('inputOnly')}
|
|
46
|
+
</button>
|
|
47
|
+
</div>
|
|
48
|
+
}
|
|
49
|
+
width={400}
|
|
50
|
+
>
|
|
51
|
+
<p>{files?.map(f => f.path).join(', ')}</p>
|
|
52
|
+
</Modal>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default DropFileModal
|
|
@@ -16,7 +16,8 @@ export default class TerminalCmdSuggestions extends Component {
|
|
|
16
16
|
aiSuggestions: [],
|
|
17
17
|
cmdIsDescription: false,
|
|
18
18
|
reverse: false,
|
|
19
|
-
cmd: ''
|
|
19
|
+
cmd: '',
|
|
20
|
+
passwordMode: false
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
componentDidMount () {
|
|
@@ -95,6 +96,9 @@ export default class TerminalCmdSuggestions extends Component {
|
|
|
95
96
|
}
|
|
96
97
|
|
|
97
98
|
openSuggestions = (cursorPosition, cmd) => {
|
|
99
|
+
if (this.state.passwordMode) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
98
102
|
if (!this.state.showSuggestions) {
|
|
99
103
|
document.addEventListener('click', this.handleClickOutside)
|
|
100
104
|
document.addEventListener('keydown', this.handleKeyDown)
|
|
@@ -128,7 +132,45 @@ export default class TerminalCmdSuggestions extends Component {
|
|
|
128
132
|
showSuggestions: true,
|
|
129
133
|
cursorPosition: position,
|
|
130
134
|
cmd,
|
|
131
|
-
reverse
|
|
135
|
+
reverse,
|
|
136
|
+
passwordMode: false
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
openPasswordSuggestions = (cursorPosition) => {
|
|
141
|
+
if (!this.state.showSuggestions) {
|
|
142
|
+
document.addEventListener('click', this.handleClickOutside)
|
|
143
|
+
document.addEventListener('keydown', this.handleKeyDown)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const {
|
|
147
|
+
left,
|
|
148
|
+
top,
|
|
149
|
+
cellHeight
|
|
150
|
+
} = cursorPosition
|
|
151
|
+
const w = window.innerWidth
|
|
152
|
+
const h = window.innerHeight
|
|
153
|
+
|
|
154
|
+
const position = {}
|
|
155
|
+
const reverse = top > h / 2
|
|
156
|
+
|
|
157
|
+
if (left > w / 2) {
|
|
158
|
+
position.right = w - left
|
|
159
|
+
} else {
|
|
160
|
+
position.left = left
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (reverse) {
|
|
164
|
+
position.bottom = h - top + cellHeight * 1.5
|
|
165
|
+
} else {
|
|
166
|
+
position.top = top + cellHeight
|
|
167
|
+
}
|
|
168
|
+
this.setState({
|
|
169
|
+
showSuggestions: true,
|
|
170
|
+
cursorPosition: position,
|
|
171
|
+
cmd: '',
|
|
172
|
+
reverse,
|
|
173
|
+
passwordMode: true
|
|
132
174
|
})
|
|
133
175
|
}
|
|
134
176
|
|
|
@@ -146,7 +188,8 @@ export default class TerminalCmdSuggestions extends Component {
|
|
|
146
188
|
}
|
|
147
189
|
this.setState({
|
|
148
190
|
showSuggestions: false,
|
|
149
|
-
aiSuggestions: []
|
|
191
|
+
aiSuggestions: [],
|
|
192
|
+
passwordMode: false
|
|
150
193
|
})
|
|
151
194
|
}
|
|
152
195
|
|
|
@@ -172,18 +215,33 @@ export default class TerminalCmdSuggestions extends Component {
|
|
|
172
215
|
const terminal = refs.get('term-' + activeTabId)
|
|
173
216
|
if (!terminal) {
|
|
174
217
|
console.log('No active terminal found')
|
|
218
|
+
this.closeSuggestions()
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (item.type === 'PW') {
|
|
223
|
+
try {
|
|
224
|
+
// Send password + Enter directly, no backspace needed
|
|
225
|
+
terminal.attachAddon._sendData(item.command + '\r')
|
|
226
|
+
terminal.attachAddon._passwordPromptDetected = false
|
|
227
|
+
terminal.attachAddon._lastOutputLine = ''
|
|
228
|
+
} catch (e) {
|
|
229
|
+
console.error('Failed to send password:', e)
|
|
230
|
+
}
|
|
231
|
+
terminal.term.focus()
|
|
232
|
+
this.closeSuggestions()
|
|
175
233
|
return
|
|
176
234
|
}
|
|
177
235
|
|
|
178
|
-
// const titleElement = domEvent.target.closest('.ant-menu-title-content')
|
|
179
|
-
// const command = titleElement?.firstChild?.textContent
|
|
180
236
|
const { command } = item
|
|
181
|
-
|
|
237
|
+
// Read current input from buffer directly to avoid stale state
|
|
238
|
+
// (onData fires before echo, so this.state.cmd may lag behind)
|
|
239
|
+
const currentInput = terminal.getCurrentInput() || ''
|
|
182
240
|
let txt = ''
|
|
183
|
-
if (
|
|
184
|
-
txt = command.slice(
|
|
241
|
+
if (currentInput && command.startsWith(currentInput)) {
|
|
242
|
+
txt = command.slice(currentInput.length)
|
|
185
243
|
} else {
|
|
186
|
-
const pre = '\b'.repeat(
|
|
244
|
+
const pre = '\b'.repeat(currentInput.length)
|
|
187
245
|
txt = pre + command
|
|
188
246
|
}
|
|
189
247
|
terminal.attachAddon._sendData(txt)
|
|
@@ -209,6 +267,24 @@ export default class TerminalCmdSuggestions extends Component {
|
|
|
209
267
|
})
|
|
210
268
|
}
|
|
211
269
|
|
|
270
|
+
getPasswordSuggestions = () => {
|
|
271
|
+
const bookmarks = window.store.bookmarks || []
|
|
272
|
+
const seen = new Set()
|
|
273
|
+
const res = []
|
|
274
|
+
for (const b of bookmarks) {
|
|
275
|
+
if (b.password && !seen.has(b.password)) {
|
|
276
|
+
seen.add(b.password)
|
|
277
|
+
res.push({
|
|
278
|
+
id: uid(),
|
|
279
|
+
command: b.password,
|
|
280
|
+
type: 'PW',
|
|
281
|
+
hint: [b.username, b.host].filter(Boolean).join('@')
|
|
282
|
+
})
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return this.state.reverse ? res.reverse() : res
|
|
286
|
+
}
|
|
287
|
+
|
|
212
288
|
getSuggestions = () => {
|
|
213
289
|
const uniqueCommands = new Set()
|
|
214
290
|
const {
|
|
@@ -274,17 +350,19 @@ export default class TerminalCmdSuggestions extends Component {
|
|
|
274
350
|
}
|
|
275
351
|
|
|
276
352
|
render () {
|
|
277
|
-
const { showSuggestions, cursorPosition, reverse } = this.state
|
|
353
|
+
const { showSuggestions, cursorPosition, reverse, passwordMode } = this.state
|
|
278
354
|
if (!showSuggestions) {
|
|
279
355
|
return null
|
|
280
356
|
}
|
|
281
|
-
const suggestions =
|
|
357
|
+
const suggestions = passwordMode
|
|
358
|
+
? this.getPasswordSuggestions()
|
|
359
|
+
: this.getSuggestions()
|
|
282
360
|
const cls = classnames('terminal-suggestions-wrap', {
|
|
283
361
|
reverse
|
|
284
362
|
})
|
|
285
363
|
return (
|
|
286
364
|
<div className={cls} style={cursorPosition}>
|
|
287
|
-
{this.renderSticky('top')}
|
|
365
|
+
{!passwordMode && this.renderSticky('top')}
|
|
288
366
|
<div className='terminal-suggestions-list'>
|
|
289
367
|
{
|
|
290
368
|
suggestions.map(item => {
|
|
@@ -299,7 +377,7 @@ export default class TerminalCmdSuggestions extends Component {
|
|
|
299
377
|
})
|
|
300
378
|
}
|
|
301
379
|
</div>
|
|
302
|
-
{this.renderSticky('bottom')}
|
|
380
|
+
{!passwordMode && this.renderSticky('bottom')}
|
|
303
381
|
</div>
|
|
304
382
|
)
|
|
305
383
|
}
|