@electerm/electerm-react 1.72.18 → 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.
@@ -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
- inputRef.current.focus()
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
- termSearch,
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
- window.store.termSearch = e.target.value
86
- this.prev()
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
- ?.searchAddon.clearDecorations()
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>
@@ -135,3 +135,11 @@
135
135
  .suggestion-delete
136
136
  margin-left 5px
137
137
  visibility hidden
138
+
139
+ .term-search-bar
140
+ position absolute
141
+ right 0
142
+ top 0
143
+ bottom 0
144
+ background transparent
145
+ width 16px
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "1.72.18",
3
+ "version": "1.72.26",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",