@electerm/electerm-react 1.72.16 → 1.72.26
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 +1 -1
- package/client/components/common/input-auto-focus.jsx +11 -3
- package/client/components/session/session.jsx +1 -3
- package/client/components/terminal/term-search.jsx +13 -8
- package/client/components/terminal/terminal-search-bar.jsx +48 -0
- package/client/components/terminal/terminal.jsx +26 -4
- package/client/components/terminal/terminal.styl +8 -0
- package/package.json +1 -1
|
@@ -4,14 +4,22 @@ import {
|
|
|
4
4
|
} from 'antd'
|
|
5
5
|
|
|
6
6
|
export default function InputAutoFocus (props) {
|
|
7
|
-
const { type, ...rest } = props
|
|
7
|
+
const { type, selectall = false, ...rest } = props
|
|
8
8
|
const inputRef = useRef(null)
|
|
9
|
+
const isFirstRender = useRef(true)
|
|
9
10
|
|
|
10
11
|
useEffect(() => {
|
|
11
12
|
if (inputRef.current) {
|
|
12
|
-
|
|
13
|
+
const { value } = props
|
|
14
|
+
if (value && selectall && isFirstRender.current) {
|
|
15
|
+
inputRef.current.focus()
|
|
16
|
+
inputRef.current.setSelectionRange(0, value.length)
|
|
17
|
+
isFirstRender.current = false
|
|
18
|
+
} else {
|
|
19
|
+
inputRef.current.focus()
|
|
20
|
+
}
|
|
13
21
|
}
|
|
14
|
-
}, [props.value])
|
|
22
|
+
}, [props.value, props.selectall])
|
|
15
23
|
|
|
16
24
|
let InputComponent
|
|
17
25
|
switch (type) {
|
|
@@ -30,8 +30,7 @@ import {
|
|
|
30
30
|
terminalRdpType,
|
|
31
31
|
terminalVncType,
|
|
32
32
|
terminalWebType,
|
|
33
|
-
terminalTelnetType
|
|
34
|
-
splitMap
|
|
33
|
+
terminalTelnetType
|
|
35
34
|
} from '../../common/constants'
|
|
36
35
|
import { SplitViewIcon } from '../icons/split-view'
|
|
37
36
|
import { refs } from '../common/ref'
|
|
@@ -521,7 +520,6 @@ export default class SessionWrapper extends Component {
|
|
|
521
520
|
|
|
522
521
|
renderBroadcastIcon = () => {
|
|
523
522
|
if (
|
|
524
|
-
this.props.layout === splitMap.c1 ||
|
|
525
523
|
this.isSshDisabled()
|
|
526
524
|
) {
|
|
527
525
|
return null
|
|
@@ -60,15 +60,14 @@ export default class TermSearch extends PureComponent {
|
|
|
60
60
|
setTimeout(window.store.focus, 200)
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
prev = () => {
|
|
63
|
+
prev = (v = this.props.termSearch) => {
|
|
64
64
|
const {
|
|
65
65
|
activeTabId,
|
|
66
|
-
termSearch,
|
|
67
66
|
termSearchOptions
|
|
68
67
|
} = this.props
|
|
69
68
|
refs.get('term-' + activeTabId)
|
|
70
69
|
?.searchPrev(
|
|
71
|
-
|
|
70
|
+
v,
|
|
72
71
|
copy(termSearchOptions)
|
|
73
72
|
)
|
|
74
73
|
}
|
|
@@ -82,13 +81,18 @@ export default class TermSearch extends PureComponent {
|
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
handleChange = e => {
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
const v = e.target.value
|
|
85
|
+
window.store.termSearch = v
|
|
86
|
+
this.prev(v)
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
clearSearch = () => {
|
|
90
|
-
refs.get('term-' + this.props.activeTabId)
|
|
91
|
-
|
|
90
|
+
const term = refs.get('term-' + this.props.activeTabId)
|
|
91
|
+
term?.searchAddon.clearDecorations()
|
|
92
|
+
term.setState({
|
|
93
|
+
searchResults: [],
|
|
94
|
+
matchIndex: -1
|
|
95
|
+
})
|
|
92
96
|
}
|
|
93
97
|
|
|
94
98
|
close = () => {
|
|
@@ -201,7 +205,8 @@ export default class TermSearch extends PureComponent {
|
|
|
201
205
|
onChange: this.handleChange,
|
|
202
206
|
suffix: this.renderSuffix(),
|
|
203
207
|
onPressEnter: this.next,
|
|
204
|
-
addonAfter: this.renderAfter()
|
|
208
|
+
addonAfter: this.renderAfter(),
|
|
209
|
+
selectall: true
|
|
205
210
|
}
|
|
206
211
|
return (
|
|
207
212
|
<div className='term-search-wrap'>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from 'react'
|
|
2
|
+
|
|
3
|
+
export default function SearchResultBar ({
|
|
4
|
+
matches,
|
|
5
|
+
totalLines,
|
|
6
|
+
matchIndex,
|
|
7
|
+
height
|
|
8
|
+
}) {
|
|
9
|
+
const canvasRef = useRef(null)
|
|
10
|
+
const drawSearchResults = () => {
|
|
11
|
+
const canvas = canvasRef.current
|
|
12
|
+
if (!canvas) return
|
|
13
|
+
|
|
14
|
+
const container = canvas.parentElement
|
|
15
|
+
const containerHeight = container.clientHeight
|
|
16
|
+
const dpr = window.devicePixelRatio || 1
|
|
17
|
+
|
|
18
|
+
// Set both canvas dimensions and style
|
|
19
|
+
canvas.height = containerHeight * dpr
|
|
20
|
+
canvas.width = 16 * dpr
|
|
21
|
+
|
|
22
|
+
const ctx = canvas.getContext('2d')
|
|
23
|
+
// Scale the context to account for the pixel ratio
|
|
24
|
+
ctx.scale(dpr, dpr)
|
|
25
|
+
|
|
26
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
|
27
|
+
matches.forEach((match, index) => {
|
|
28
|
+
const y = (match / totalLines) * containerHeight
|
|
29
|
+
ctx.fillStyle = index === matchIndex ? 'rgba(243, 67, 9, 0.5)' : 'rgba(243, 196, 9, 0.5)'
|
|
30
|
+
ctx.fillRect(0, y, 16, 2)
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
drawSearchResults()
|
|
36
|
+
}, [matches, totalLines, matchIndex])
|
|
37
|
+
|
|
38
|
+
if (!matches.length) {
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<canvas
|
|
44
|
+
ref={canvasRef}
|
|
45
|
+
className='term-search-bar'
|
|
46
|
+
/>
|
|
47
|
+
)
|
|
48
|
+
}
|
|
@@ -52,6 +52,7 @@ import * as fs from './fs.js'
|
|
|
52
52
|
import iconsMap from '../sys-menu/icons-map.jsx'
|
|
53
53
|
import { refs, refsStatic } from '../common/ref.js'
|
|
54
54
|
import createDefaultLogPath from '../../common/default-log-path.js'
|
|
55
|
+
import SearchResultBar from './terminal-search-bar'
|
|
55
56
|
|
|
56
57
|
const e = window.translate
|
|
57
58
|
|
|
@@ -64,7 +65,10 @@ class Term extends Component {
|
|
|
64
65
|
saveTerminalLogToFile: !!this.props.config.saveTerminalLogToFile,
|
|
65
66
|
addTimeStampToTermLog: !!this.props.config.addTimeStampToTermLog,
|
|
66
67
|
passType: 'password',
|
|
67
|
-
lines: []
|
|
68
|
+
lines: [],
|
|
69
|
+
searchResults: [],
|
|
70
|
+
matchIndex: -1,
|
|
71
|
+
totalLines: 0
|
|
68
72
|
}
|
|
69
73
|
this.id = `term-${this.props.tab.id}`
|
|
70
74
|
refs.add(this.id, this)
|
|
@@ -703,6 +707,20 @@ clear\r`
|
|
|
703
707
|
termSearchMatchCount: resultCount,
|
|
704
708
|
termSearchMatchIndex: resultIndex
|
|
705
709
|
})
|
|
710
|
+
|
|
711
|
+
this.updateSearchResults(resultIndex)
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
updateSearchResults = (resultIndex) => {
|
|
715
|
+
const matches = this.searchAddon._highlightDecorations.map((highlight, i) => {
|
|
716
|
+
return highlight.match.row
|
|
717
|
+
})
|
|
718
|
+
|
|
719
|
+
this.setState({
|
|
720
|
+
searchResults: matches,
|
|
721
|
+
matchIndex: resultIndex,
|
|
722
|
+
totalLines: this.term.buffer.active.length
|
|
723
|
+
})
|
|
706
724
|
}
|
|
707
725
|
|
|
708
726
|
searchPrev = (searchInput, options) => {
|
|
@@ -1161,10 +1179,7 @@ clear\r`
|
|
|
1161
1179
|
}
|
|
1162
1180
|
|
|
1163
1181
|
canReceiveBroadcast = (termRef) => {
|
|
1164
|
-
const tabId = termRef.props?.tab?.id
|
|
1165
|
-
const isActiveInBatch = termRef.props.currentBatchTabId === tabId
|
|
1166
1182
|
return (
|
|
1167
|
-
isActiveInBatch &&
|
|
1168
1183
|
termRef.socket &&
|
|
1169
1184
|
termRef.props?.tab.pane === paneMap.terminal
|
|
1170
1185
|
)
|
|
@@ -1349,6 +1364,12 @@ clear\r`
|
|
|
1349
1364
|
},
|
|
1350
1365
|
trigger: this.props.config.pasteWhenContextMenu ? [] : ['contextMenu']
|
|
1351
1366
|
}
|
|
1367
|
+
const barProps = {
|
|
1368
|
+
matchIndex: this.state.matchIndex,
|
|
1369
|
+
matches: this.state.searchResults,
|
|
1370
|
+
totalLines: this.state.totalLines,
|
|
1371
|
+
height
|
|
1372
|
+
}
|
|
1352
1373
|
return (
|
|
1353
1374
|
<Dropdown {...dropdownProps}>
|
|
1354
1375
|
<div
|
|
@@ -1361,6 +1382,7 @@ clear\r`
|
|
|
1361
1382
|
lines={this.state.lines}
|
|
1362
1383
|
close={this.closeNormalBuffer}
|
|
1363
1384
|
/>
|
|
1385
|
+
<SearchResultBar {...barProps} />
|
|
1364
1386
|
<Spin className='loading-wrapper' spinning={loading} />
|
|
1365
1387
|
</div>
|
|
1366
1388
|
</Dropdown>
|