@electerm/electerm-react 2.10.27 → 2.11.16

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.
Files changed (38) hide show
  1. package/client/common/default-setting.js +1 -0
  2. package/client/common/fetch.jsx +1 -1
  3. package/client/common/has-active-input.js +1 -1
  4. package/client/common/parse-quick-connect.js +438 -0
  5. package/client/components/ai/ai-chat-history-item.jsx +1 -1
  6. package/client/components/ai/ai-config.jsx +1 -1
  7. package/client/components/bg/css-overwrite.jsx +5 -1
  8. package/client/components/bookmark-form/ai-bookmark-form.jsx +40 -3
  9. package/client/components/bookmark-form/bookmark-form.styl +21 -0
  10. package/client/components/bookmark-form/bookmark-from-history-modal.jsx +141 -0
  11. package/client/components/bookmark-form/tree-select.jsx +72 -23
  12. package/client/components/common/input-context-menu.jsx +13 -5
  13. package/client/components/main/main.jsx +3 -0
  14. package/client/components/rdp/rdp-session.jsx +4 -8
  15. package/client/components/rdp/rdp.styl +15 -0
  16. package/client/components/setting-panel/bookmark-tree-list.jsx +1 -0
  17. package/client/components/setting-panel/deep-link-control.jsx +1 -1
  18. package/client/components/setting-panel/list.styl +10 -4
  19. package/client/components/setting-panel/setting-terminal.jsx +3 -2
  20. package/client/components/sftp/list-table-ui.jsx +29 -2
  21. package/client/components/sftp/paged-list.jsx +3 -8
  22. package/client/components/sidebar/history-item.jsx +13 -1
  23. package/client/components/sidebar/index.jsx +13 -10
  24. package/client/components/spice/spice.styl +7 -0
  25. package/client/components/tabs/add-btn-menu.jsx +2 -0
  26. package/client/components/tabs/no-session.jsx +25 -9
  27. package/client/components/tabs/no-session.styl +21 -0
  28. package/client/components/tabs/quick-connect.jsx +127 -0
  29. package/client/components/tabs/tabs.styl +1 -19
  30. package/client/components/terminal/highlight-addon.js +11 -0
  31. package/client/components/terminal/terminal.jsx +16 -1
  32. package/client/components/terminal/trzsz-client.js +6 -0
  33. package/client/components/terminal/xterm-loader.js +11 -0
  34. package/client/components/terminal-info/run-cmd.jsx +2 -1
  35. package/client/store/load-data.js +5 -1
  36. package/client/store/sync.js +1 -0
  37. package/client/store/tab.js +9 -0
  38. package/package.json +1 -1
@@ -8,12 +8,13 @@ import {
8
8
  UpCircleOutlined,
9
9
  BarsOutlined,
10
10
  AppstoreOutlined,
11
- RobotOutlined
11
+ ThunderboltOutlined
12
12
  } from '@ant-design/icons'
13
- import { Tooltip } from 'antd'
13
+ import { Tooltip, Popover } from 'antd'
14
14
  import SideBarPanel from './sidebar-panel'
15
15
  import TransferList from './transfer-list'
16
16
  import MenuBtn from '../sys-menu/menu-btn'
17
+ import QuickConnect from '../tabs/quick-connect'
17
18
  import {
18
19
  sidebarWidth,
19
20
  settingMap,
@@ -87,7 +88,6 @@ export default function Sidebar (props) {
87
88
 
88
89
  const {
89
90
  onNewSsh,
90
- onNewSshAI,
91
91
  openSetting,
92
92
  openAbout,
93
93
  openSettingSync,
@@ -144,14 +144,17 @@ export default function Sidebar (props) {
144
144
  onClick={onNewSsh}
145
145
  />
146
146
  </SideIcon>
147
- <SideIcon
148
- title={e('createBookmarkByAI')}
147
+ <Popover
148
+ content={<QuickConnect inputOnly />}
149
+ trigger='click'
150
+ placement='right'
149
151
  >
150
- <RobotOutlined
151
- className='font20 iblock control-icon'
152
- onClick={onNewSshAI}
153
- />
154
- </SideIcon>
152
+ <div className='control-icon-wrap' title={e('quickConnect')}>
153
+ <ThunderboltOutlined
154
+ className='font20 iblock control-icon'
155
+ />
156
+ </div>
157
+ </Popover>
155
158
  <SideIcon
156
159
  title={e(settingMap.bookmarks)}
157
160
  active={bookmarksActive}
@@ -2,3 +2,10 @@
2
2
  canvas
3
3
  width: 100% !important
4
4
  object-fit: contain
5
+ .spice-scroll-wrapper
6
+ display block
7
+ .spice-scroll-wrapper
8
+ display: flex
9
+ flex-direction: column
10
+ justify-content: center
11
+ align-items: center
@@ -10,6 +10,7 @@ import {
10
10
  } from '@ant-design/icons'
11
11
  import BookmarksList from '../sidebar/bookmark-select'
12
12
  import DragHandle from '../common/drag-handle'
13
+ import QuickConnect from './quick-connect'
13
14
 
14
15
  const e = window.translate
15
16
 
@@ -87,6 +88,7 @@ export default function AddBtnMenu ({
87
88
  >
88
89
  <RobotOutlined /> {e('createBookmarkByAI')}
89
90
  </div>
91
+ <QuickConnect batch={batch} inputOnly />
90
92
  </div>
91
93
  <div className='add-menu-list'>
92
94
  <BookmarksList
@@ -1,6 +1,9 @@
1
1
  import { Button } from 'antd'
2
+ import { RobotOutlined } from '@ant-design/icons'
2
3
  import LogoElem from '../common/logo-elem.jsx'
3
4
  import HistoryPanel from '../sidebar/history'
5
+ import QuickConnect from './quick-connect'
6
+ import './no-session.styl'
4
7
 
5
8
  const e = window.translate
6
9
 
@@ -13,11 +16,15 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
13
16
  const handleClick = () => {
14
17
  window.openTabBatch = batch
15
18
  }
19
+
20
+ const handleCreateAIBookmark = () => {
21
+ window.store.onNewSshAI()
22
+ }
23
+
16
24
  const newTabDom = window.store.hasNodePty
17
25
  ? (
18
26
  <Button
19
27
  onClick={onNewTab}
20
- size='large'
21
28
  className='mg1r mg1b add-new-tab-btn'
22
29
  >
23
30
  {e('newTab')}
@@ -26,14 +33,23 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
26
33
  : null
27
34
  return (
28
35
  <div className='no-sessions electerm-logo-bg' {...props}>
29
- {newTabDom}
30
- <Button
31
- onClick={onNewSsh}
32
- size='large'
33
- className='mg1r mg1b'
34
- >
35
- {e('newBookmark')}
36
- </Button>
36
+ <div className='pd1b'>
37
+ {newTabDom}
38
+ <Button
39
+ onClick={onNewSsh}
40
+ className='mg1r mg1b'
41
+ >
42
+ {e('newBookmark')}
43
+ </Button>
44
+ <Button
45
+ onClick={handleCreateAIBookmark}
46
+ className='mg1r mg1b'
47
+ icon={<RobotOutlined />}
48
+ >
49
+ {e('createBookmarkByAI')}
50
+ </Button>
51
+ <QuickConnect batch={batch} />
52
+ </div>
37
53
  <div className='pd3'>
38
54
  <LogoElem />
39
55
  </div>
@@ -0,0 +1,21 @@
1
+ .no-sessions
2
+ background var(--main)
3
+ text-align center
4
+ padding 50px 0
5
+ position absolute
6
+ top 36px
7
+ left 0
8
+ right 0
9
+ bottom 0
10
+ z-index 11
11
+ .no-session-history
12
+ position absolute
13
+ top 320px
14
+ bottom 80px
15
+ left 0
16
+ right 0
17
+ .sidebar-panel-history
18
+ overflow-y auto
19
+ .pd2x
20
+ width 400px
21
+ margin 0 auto
@@ -0,0 +1,127 @@
1
+ import { useState, useRef, useEffect } from 'react'
2
+ import { Button, Space } from 'antd'
3
+ import { ArrowRightOutlined, ThunderboltOutlined } from '@ant-design/icons'
4
+ import message from '../common/message'
5
+ import InputAutoFocus from '../common/input-auto-focus'
6
+ import HelpIcon from '../common/help-icon'
7
+
8
+ const e = window.translate
9
+
10
+ /**
11
+ * Connect using parsed options
12
+ * @param {object} opts - Connection options
13
+ * @param {number} batch - Batch number
14
+ */
15
+ function connectWithOptions (opts, batch) {
16
+ const { store } = window
17
+ const tabOptions = {
18
+ ...opts,
19
+ from: 'quickConnect',
20
+ batch
21
+ }
22
+
23
+ delete window.openTabBatch
24
+ store.addTab(tabOptions)
25
+ }
26
+
27
+ export default function QuickConnect ({ batch, inputOnly }) {
28
+ const [showInput, setShowInput] = useState(false)
29
+ const [inputValue, setInputValue] = useState('')
30
+ const inputRef = useRef(null)
31
+
32
+ useEffect(() => {
33
+ if (showInput && inputRef.current) {
34
+ inputRef.current.focus()
35
+ }
36
+ }, [showInput])
37
+
38
+ // When inputOnly is true, always show the input
39
+ useEffect(() => {
40
+ if (inputOnly) {
41
+ setShowInput(true)
42
+ }
43
+ }, [inputOnly])
44
+
45
+ const handleToggle = () => {
46
+ setShowInput(!showInput)
47
+ if (showInput) {
48
+ setInputValue('')
49
+ }
50
+ }
51
+
52
+ function handleChange (e) {
53
+ setInputValue(e.target.value)
54
+ }
55
+
56
+ const handleConnect = () => {
57
+ if (!inputValue.trim()) {
58
+ return
59
+ }
60
+
61
+ const opts = window.store.parseQuickConnect(inputValue)
62
+ if (!opts) {
63
+ return message.error('Format error, please check the input', 10)
64
+ }
65
+
66
+ connectWithOptions(opts, batch)
67
+ setInputValue('')
68
+ setShowInput(false)
69
+ }
70
+
71
+ function renderInput () {
72
+ if (!showInput && !inputOnly) {
73
+ return null
74
+ }
75
+ const inputProps = {
76
+ ref: inputRef,
77
+ value: inputValue,
78
+ onChange: handleChange,
79
+ className: 'width-100 quick-connect-input',
80
+ onPressEnter: handleConnect,
81
+ placeholder: 'ssh|rdp|vnc|spice|serial|http|https://[username]:[password]@host:port?opts={...}',
82
+ prefix: inputOnly ? <HelpIcon link={wiki} /> : undefined
83
+ }
84
+ const iconProps = {
85
+ onClick: handleConnect,
86
+ title: e('connect'),
87
+ icon: <ArrowRightOutlined />
88
+ }
89
+ const iconsProps1 = {
90
+ icon: <ThunderboltOutlined />
91
+ }
92
+ return (
93
+ <Space.Compact className='pd1y pd2x width-100'>
94
+ <Button
95
+ {...iconsProps1}
96
+ />
97
+ <InputAutoFocus {...inputProps} />
98
+ <Button
99
+ {...iconProps}
100
+ />
101
+ </Space.Compact>
102
+ )
103
+ }
104
+ const wiki = 'https://github.com/electerm/electerm/wiki/quick-connect'
105
+
106
+ // If inputOnly is true, don't show the button, just render input directly
107
+ if (inputOnly) {
108
+ return renderInput()
109
+ }
110
+
111
+ const btnProps = {
112
+ onClick: handleToggle,
113
+ icon: <ThunderboltOutlined />,
114
+ title: e('quickConnect')
115
+ }
116
+ return (
117
+ <>
118
+ <Button
119
+ {...btnProps}
120
+ >
121
+ <span className='mg1r'>{e('quickConnect')}</span>
122
+ <HelpIcon link={wiki} />
123
+ </Button>
124
+ {renderInput()}
125
+ </>
126
+ )
127
+ }
@@ -218,30 +218,12 @@
218
218
  .tab-title-tag
219
219
  margin-right 5px
220
220
  font-size 14px
221
- .no-sessions
222
- background var(--main)
223
- text-align center
224
- padding 50px 0
225
- position absolute
226
- top 36px
227
- left 0
228
- right 0
229
- bottom 0
230
- z-index 11
221
+
231
222
  .tab-count
232
223
  border-radius 10px 2px 2px 10px
233
224
  padding 0 4px
234
225
  height 20px
235
226
  line-height 21px
236
- .no-session-history
237
- position absolute
238
- top 280px
239
- bottom 80px
240
- left 0
241
- right 0
242
- .sidebar-panel-history .pd2x
243
- width 400px
244
- margin 0 auto
245
227
 
246
228
  .ssh-tunnel-list-wrapper
247
229
  max-height 300px
@@ -36,6 +36,11 @@ export class KeywordHighlighterAddon {
36
36
  return colorMap[color] || colorMap.red
37
37
  }
38
38
 
39
+ containsDcsSequence = (text) => {
40
+ const ESC = String.fromCharCode(27)
41
+ return text.includes(ESC + 'P')
42
+ }
43
+
39
44
  escape = str => {
40
45
  return str.replace(/\\x1B/g, '\\x1B')
41
46
  .replace(/\033/g, '\\033')
@@ -96,6 +101,12 @@ export class KeywordHighlighterAddon {
96
101
  this.originalWrite = terminal.write.bind(terminal)
97
102
  const self = this
98
103
  terminal.write = function (data) {
104
+ if (typeof data !== 'string') {
105
+ return self.originalWrite(data)
106
+ }
107
+ if (self.containsDcsSequence(data)) {
108
+ return self.originalWrite(data)
109
+ }
99
110
  self.originalWrite(
100
111
  terminal.displayRaw ? self.escape(data) : self.highlightKeywords(data)
101
112
  )
@@ -57,7 +57,8 @@ import {
57
57
  loadWebglAddon,
58
58
  loadSearchAddon,
59
59
  loadLigaturesAddon,
60
- loadUnicode11Addon
60
+ loadUnicode11Addon,
61
+ loadImageAddon
61
62
  } from './xterm-loader.js'
62
63
 
63
64
  const e = window.translate
@@ -164,6 +165,7 @@ class Term extends Component {
164
165
  this.searchAddon = null
165
166
  this.fitAddon = null
166
167
  this.cmdAddon = null
168
+ this.imageAddon = null
167
169
  // Clear the notification if it exists
168
170
  if (this.socketCloseWarning) {
169
171
  notification.destroy(this.socketCloseWarning.key)
@@ -816,6 +818,19 @@ class Term extends Component {
816
818
  this.searchAddon.onDidChangeResults(this.onSearchResultsChange)
817
819
  const Unicode11Addon = await loadUnicode11Addon()
818
820
  const unicode11Addon = new Unicode11Addon()
821
+ if (config.enableSixel !== false) {
822
+ try {
823
+ const ImageAddon = await loadImageAddon()
824
+ this.imageAddon = new ImageAddon({
825
+ enableSizeReports: false,
826
+ sixelSupport: true,
827
+ iipSupport: false
828
+ })
829
+ term.loadAddon(this.imageAddon)
830
+ } catch (err) {
831
+ console.error('load sixel addon failed', err)
832
+ }
833
+ }
819
834
  term.loadAddon(unicode11Addon)
820
835
  term.loadAddon(ligtureAddon)
821
836
  term.unicode.activeVersion = '11'
@@ -44,6 +44,12 @@ export class TrzszClient extends TransferClientBase {
44
44
  handleServerEvent (msg) {
45
45
  const { event } = msg
46
46
 
47
+ // Ignore events that arrive after the session has already ended
48
+ // (e.g. async error from cancelled transfer)
49
+ if (!this.isActive && event !== 'receive-start' && event !== 'send-start') {
50
+ return
51
+ }
52
+
47
53
  switch (event) {
48
54
  case 'receive-start':
49
55
  this.onReceiveStart()
@@ -72,6 +72,13 @@ export async function loadUnicode11Addon () {
72
72
  return window.xtermAddons.Unicode11Addon
73
73
  }
74
74
 
75
+ export async function loadImageAddon () {
76
+ if (window.xtermAddons.ImageAddon) return window.xtermAddons.ImageAddon
77
+ const mod = await import('@xterm/addon-image')
78
+ window.xtermAddons.ImageAddon = mod.ImageAddon
79
+ return window.xtermAddons.ImageAddon
80
+ }
81
+
75
82
  export function getTerminal () {
76
83
  return window.xtermAddons.Terminal
77
84
  }
@@ -107,3 +114,7 @@ export function getLigaturesAddon () {
107
114
  export function getUnicode11Addon () {
108
115
  return window.xtermAddons.Unicode11Addon
109
116
  }
117
+
118
+ export function getImageAddon () {
119
+ return window.xtermAddons.ImageAddon
120
+ }
@@ -167,6 +167,7 @@ function InfoGetter (props) {
167
167
  formatter,
168
168
  delay
169
169
  } = props.options
170
+ const { pid } = props
170
171
  const cms = cmds || [cmd]
171
172
  useEffect(() => {
172
173
  const run = async () => {
@@ -180,7 +181,7 @@ function InfoGetter (props) {
180
181
  return () => {
181
182
  clearInterval(ref)
182
183
  }
183
- }, [])
184
+ }, [pid])
184
185
  return null
185
186
  }
186
187
 
@@ -13,6 +13,7 @@ import { initWsCommon } from '../common/fetch-from-server'
13
13
  import safeParse from '../common/parse-json-safe'
14
14
  import initWatch from './watch'
15
15
  import { refsStatic } from '../components/common/ref'
16
+ import { parseQuickConnect } from '../common/parse-quick-connect'
16
17
 
17
18
  function getHost (argv, opts) {
18
19
  const arr = argv
@@ -219,7 +220,10 @@ export default (Store) => {
219
220
  Store.prototype.checkPendingDeepLink = async function () {
220
221
  const pending = await window.pre.runGlobalAsync('getPendingDeepLink')
221
222
  if (pending) {
222
- addTabFromCommandLine(window.store, pending)
223
+ window.store.addTab(pending)
223
224
  }
224
225
  }
226
+ Store.prototype.parseQuickConnect = function (url) {
227
+ return parseQuickConnect(url)
228
+ }
225
229
  }
@@ -488,6 +488,7 @@ export default (Store) => {
488
488
  'hotkey',
489
489
  'sshReadyTimeout',
490
490
  'scrollback',
491
+ 'enableSixel',
491
492
  'fontSize',
492
493
  'execWindows',
493
494
  'execMac',
@@ -341,6 +341,15 @@ export default Store => {
341
341
  const { tabs } = store
342
342
  newTab.tabCount = store.nextTabCount()
343
343
  newTab.batch = batch ?? newTab.batch ?? window.openTabBatch ?? window.store.currentLayoutBatch
344
+ if (!newTab.id) {
345
+ newTab.id = generate()
346
+ }
347
+ if (!newTab.status) {
348
+ newTab.status = statusMap.processing
349
+ }
350
+ if (!newTab.pane) {
351
+ newTab.pane = paneMap.terminal
352
+ }
344
353
  if (typeof index === 'number' && index >= 0 && index <= tabs.length) {
345
354
  tabs.splice(index, 0, newTab)
346
355
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "2.10.27",
3
+ "version": "2.11.16",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",