@electerm/electerm-react 1.34.30

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 (273) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +31 -0
  3. package/client/common/auto-complete-data-mapper.js +6 -0
  4. package/client/common/byte-format.js +14 -0
  5. package/client/common/class.js +52 -0
  6. package/client/common/clipboard.js +49 -0
  7. package/client/common/constants.js +308 -0
  8. package/client/common/create-lang-edit-link.js +7 -0
  9. package/client/common/create-title.js +29 -0
  10. package/client/common/db-fix.js +24 -0
  11. package/client/common/db.js +155 -0
  12. package/client/common/download-mirrors.js +10 -0
  13. package/client/common/download.js +16 -0
  14. package/client/common/error-handler.jsx +27 -0
  15. package/client/common/fetch-from-server.js +47 -0
  16. package/client/common/fetch.jsx +71 -0
  17. package/client/common/find-bookmark-group-id.js +15 -0
  18. package/client/common/find-parent.js +30 -0
  19. package/client/common/form-layout.js +27 -0
  20. package/client/common/fs.js +21 -0
  21. package/client/common/get-proxy.js +8 -0
  22. package/client/common/id-with-stamp.js +10 -0
  23. package/client/common/index-sorter.js +4 -0
  24. package/client/common/init-setting-item.js +32 -0
  25. package/client/common/is-absolute-path.js +3 -0
  26. package/client/common/is-ip.js +16 -0
  27. package/client/common/is-valid-path.js +7 -0
  28. package/client/common/key-control-pressed.js +13 -0
  29. package/client/common/key-pressed.js +13 -0
  30. package/client/common/key-shift-pressed.js +7 -0
  31. package/client/common/mode2permission.js +81 -0
  32. package/client/common/new-terminal.js +31 -0
  33. package/client/common/parse-int10.js +3 -0
  34. package/client/common/parse-json-safe.js +11 -0
  35. package/client/common/pass-enc.js +25 -0
  36. package/client/common/post-msg.js +3 -0
  37. package/client/common/pre.js +156 -0
  38. package/client/common/promise-timeout.js +27 -0
  39. package/client/common/resolve.js +31 -0
  40. package/client/common/run-idle.js +1 -0
  41. package/client/common/safe-local-storage.js +35 -0
  42. package/client/common/safe-name.js +19 -0
  43. package/client/common/sftp.js +74 -0
  44. package/client/common/terminal-theme.js +158 -0
  45. package/client/common/test-connection.js +12 -0
  46. package/client/common/time.js +30 -0
  47. package/client/common/to-simple-obj.js +5 -0
  48. package/client/common/track.js +7 -0
  49. package/client/common/transfer.js +76 -0
  50. package/client/common/trzsz.js +62 -0
  51. package/client/common/ui-theme.js +44 -0
  52. package/client/common/uid.js +5 -0
  53. package/client/common/update-check.js +79 -0
  54. package/client/common/upgrade.js +68 -0
  55. package/client/common/wait.js +8 -0
  56. package/client/common/ws.js +161 -0
  57. package/client/components/batch-op/batch-op.jsx +650 -0
  58. package/client/components/bookmark-form/bookmark-form.styl +8 -0
  59. package/client/components/bookmark-form/bookmark-group-tree-format.js +39 -0
  60. package/client/components/bookmark-form/encodes.js +44 -0
  61. package/client/components/bookmark-form/form-ssh-common.jsx +208 -0
  62. package/client/components/bookmark-form/form-tabs.jsx +69 -0
  63. package/client/components/bookmark-form/index.jsx +159 -0
  64. package/client/components/bookmark-form/local-form-ui.jsx +152 -0
  65. package/client/components/bookmark-form/local-form.jsx +16 -0
  66. package/client/components/bookmark-form/proxy.jsx +49 -0
  67. package/client/components/bookmark-form/quick-command-list.jsx +31 -0
  68. package/client/components/bookmark-form/quick-command.jsx +228 -0
  69. package/client/components/bookmark-form/render-auth-ssh.jsx +104 -0
  70. package/client/components/bookmark-form/render-connection-hopping.jsx +229 -0
  71. package/client/components/bookmark-form/render-delayed-scripts.jsx +88 -0
  72. package/client/components/bookmark-form/render-ssh-tunnel.jsx +116 -0
  73. package/client/components/bookmark-form/serial-form-ui.jsx +311 -0
  74. package/client/components/bookmark-form/serial-form.jsx +20 -0
  75. package/client/components/bookmark-form/sftp-enable.jsx +33 -0
  76. package/client/components/bookmark-form/ssh-form-ui.jsx +100 -0
  77. package/client/components/bookmark-form/ssh-form.jsx +348 -0
  78. package/client/components/bookmark-form/telnet-form-ui.jsx +154 -0
  79. package/client/components/bookmark-form/telnet-form.jsx +16 -0
  80. package/client/components/bookmark-form/tree-delete.jsx +87 -0
  81. package/client/components/bookmark-form/use-form-funcs.jsx +50 -0
  82. package/client/components/bookmark-form/use-quick-commands.jsx +83 -0
  83. package/client/components/bookmark-form/use-submit.jsx +77 -0
  84. package/client/components/bookmark-form/use-ui.jsx +82 -0
  85. package/client/components/bookmark-form/x11.jsx +23 -0
  86. package/client/components/common/animate-text.jsx +37 -0
  87. package/client/components/common/animate-text.styl +54 -0
  88. package/client/components/common/external-link.jsx +28 -0
  89. package/client/components/common/help-icon.jsx +25 -0
  90. package/client/components/common/highlight.jsx +23 -0
  91. package/client/components/common/highlight.styl +3 -0
  92. package/client/components/common/input-auto-focus.jsx +68 -0
  93. package/client/components/common/input-confirm.jsx +66 -0
  94. package/client/components/common/logo-elem.jsx +22 -0
  95. package/client/components/common/markdown.jsx +27 -0
  96. package/client/components/common/react-subx.jsx +1 -0
  97. package/client/components/common/resize-wrap.jsx +222 -0
  98. package/client/components/common/resize-wrap.styl +9 -0
  99. package/client/components/common/search.jsx +9 -0
  100. package/client/components/common/show-item.jsx +27 -0
  101. package/client/components/context-menu/boomarks.jsx +15 -0
  102. package/client/components/context-menu/context-menu.jsx +340 -0
  103. package/client/components/context-menu/context-menu.styl +90 -0
  104. package/client/components/context-menu/history.jsx +27 -0
  105. package/client/components/context-menu/icon-holder.jsx +5 -0
  106. package/client/components/context-menu/menu-btn.jsx +224 -0
  107. package/client/components/context-menu/sub-tab-menu.jsx +23 -0
  108. package/client/components/context-menu/tabs.jsx +22 -0
  109. package/client/components/context-menu/zoom.jsx +40 -0
  110. package/client/components/footer/batch-input.jsx +177 -0
  111. package/client/components/footer/footer-entry.jsx +141 -0
  112. package/client/components/footer/footer.styl +47 -0
  113. package/client/components/icons/match-case.jsx +10 -0
  114. package/client/components/icons/match-whole-word.jsx +13 -0
  115. package/client/components/icons/regular-exp.jsx +10 -0
  116. package/client/components/main/css-overwrite.jsx +92 -0
  117. package/client/components/main/error-wrapper.jsx +59 -0
  118. package/client/components/main/index.jsx +11 -0
  119. package/client/components/main/loading.jsx +25 -0
  120. package/client/components/main/main.jsx +149 -0
  121. package/client/components/main/term-fullscreen-control.jsx +21 -0
  122. package/client/components/main/term-fullscreen.styl +27 -0
  123. package/client/components/main/ui-theme.jsx +31 -0
  124. package/client/components/main/upgrade.jsx +351 -0
  125. package/client/components/main/upgrade.styl +27 -0
  126. package/client/components/main/wrapper.styl +41 -0
  127. package/client/components/quick-commands/qm.styl +29 -0
  128. package/client/components/quick-commands/quick-command-item.jsx +36 -0
  129. package/client/components/quick-commands/quick-command-transport-mod.jsx +54 -0
  130. package/client/components/quick-commands/quick-command-transport.jsx +12 -0
  131. package/client/components/quick-commands/quick-commands-box.jsx +233 -0
  132. package/client/components/quick-commands/quick-commands-form-elem.jsx +119 -0
  133. package/client/components/quick-commands/quick-commands-form.jsx +33 -0
  134. package/client/components/quick-commands/quick-commands-list.jsx +128 -0
  135. package/client/components/quick-commands/quick-commands-select.jsx +38 -0
  136. package/client/components/session/session.jsx +533 -0
  137. package/client/components/session/session.styl +53 -0
  138. package/client/components/session/sessions.jsx +445 -0
  139. package/client/components/setting-panel/bookmark-transport.jsx +148 -0
  140. package/client/components/setting-panel/bookmark-tree-list.jsx +14 -0
  141. package/client/components/setting-panel/col.jsx +18 -0
  142. package/client/components/setting-panel/list.jsx +186 -0
  143. package/client/components/setting-panel/list.styl +33 -0
  144. package/client/components/setting-panel/on-tree-drop.js +222 -0
  145. package/client/components/setting-panel/setting-modal.jsx +163 -0
  146. package/client/components/setting-panel/setting-wrap.jsx +37 -0
  147. package/client/components/setting-panel/setting-wrap.styl +52 -0
  148. package/client/components/setting-panel/setting.jsx +858 -0
  149. package/client/components/setting-panel/setting.styl +4 -0
  150. package/client/components/setting-panel/start-session-select.jsx +91 -0
  151. package/client/components/setting-panel/tab-bookmarks.jsx +37 -0
  152. package/client/components/setting-panel/tab-history.jsx +44 -0
  153. package/client/components/setting-panel/tab-quick-commands.jsx +38 -0
  154. package/client/components/setting-panel/tab-settings.jsx +42 -0
  155. package/client/components/setting-panel/tab-themes.jsx +34 -0
  156. package/client/components/setting-panel/tree-list.jsx +978 -0
  157. package/client/components/setting-panel/tree-list.styl +57 -0
  158. package/client/components/setting-sync/data-import.jsx +65 -0
  159. package/client/components/setting-sync/setting-sync-form.jsx +271 -0
  160. package/client/components/setting-sync/setting-sync.jsx +81 -0
  161. package/client/components/setting-sync/sync.styl +7 -0
  162. package/client/components/sftp/address-bar.jsx +139 -0
  163. package/client/components/sftp/address-bookmark-item.jsx +47 -0
  164. package/client/components/sftp/address-bookmark.jsx +81 -0
  165. package/client/components/sftp/address-bookmark.styl +8 -0
  166. package/client/components/sftp/confirm-modal.jsx +184 -0
  167. package/client/components/sftp/file-icon.jsx +22 -0
  168. package/client/components/sftp/file-item.jsx +1226 -0
  169. package/client/components/sftp/file-mode-modal.jsx +205 -0
  170. package/client/components/sftp/file-props-modal.jsx +211 -0
  171. package/client/components/sftp/file-read.js +81 -0
  172. package/client/components/sftp/list-table-ui.jsx +547 -0
  173. package/client/components/sftp/owner-list.js +97 -0
  174. package/client/components/sftp/paged-list.jsx +60 -0
  175. package/client/components/sftp/permission-render.jsx +42 -0
  176. package/client/components/sftp/sftp-entry.jsx +1069 -0
  177. package/client/components/sftp/sftp.styl +217 -0
  178. package/client/components/sftp/transfer-common.js +9 -0
  179. package/client/components/sftp/transfer-conflict.jsx +315 -0
  180. package/client/components/sftp/transfer-speed-format.js +60 -0
  181. package/client/components/sftp/transfer-tag.jsx +40 -0
  182. package/client/components/sftp/transfer-tag.styl +11 -0
  183. package/client/components/sftp/transfer.styl +55 -0
  184. package/client/components/sftp/transport-action.jsx +410 -0
  185. package/client/components/sftp/transport-entry.jsx +108 -0
  186. package/client/components/sftp/transport-types.js +8 -0
  187. package/client/components/sftp/transports-action.jsx +111 -0
  188. package/client/components/sftp/transports-ui.jsx +93 -0
  189. package/client/components/sftp/zip.js +42 -0
  190. package/client/components/sidebar/bookmark-select.jsx +48 -0
  191. package/client/components/sidebar/bookmark.jsx +82 -0
  192. package/client/components/sidebar/history.jsx +66 -0
  193. package/client/components/sidebar/index.jsx +230 -0
  194. package/client/components/sidebar/info-modal.jsx +250 -0
  195. package/client/components/sidebar/info.styl +27 -0
  196. package/client/components/sidebar/side-icon.jsx +25 -0
  197. package/client/components/sidebar/sidebar.styl +128 -0
  198. package/client/components/sidebar/transfer-history-modal.jsx +110 -0
  199. package/client/components/sidebar/transfer-history.styl +3 -0
  200. package/client/components/sidebar/transfer-list-control.jsx +205 -0
  201. package/client/components/sidebar/transfer-list.jsx +55 -0
  202. package/client/components/sidebar/transfer-modal.jsx +76 -0
  203. package/client/components/sidebar/transfer.styl +8 -0
  204. package/client/components/sidebar/transport-ui.jsx +109 -0
  205. package/client/components/tabs/index.jsx +320 -0
  206. package/client/components/tabs/tab.jsx +427 -0
  207. package/client/components/tabs/tabs.styl +220 -0
  208. package/client/components/tabs/window-control.jsx +55 -0
  209. package/client/components/terminal/attach-addon-custom.js +70 -0
  210. package/client/components/terminal/build-ls-term-id.js +5 -0
  211. package/client/components/terminal/index.jsx +1358 -0
  212. package/client/components/terminal/normal-buffer.jsx +33 -0
  213. package/client/components/terminal/term-search.jsx +224 -0
  214. package/client/components/terminal/term-search.styl +15 -0
  215. package/client/components/terminal/terminal-apis.js +31 -0
  216. package/client/components/terminal/terminal-interactive.jsx +148 -0
  217. package/client/components/terminal/terminal.styl +96 -0
  218. package/client/components/terminal/xterm-zmodem.js +48 -0
  219. package/client/components/terminal/zmodem-transfer.jsx +98 -0
  220. package/client/components/terminal/zmodem.styl +14 -0
  221. package/client/components/terminal-info/activity.jsx +54 -0
  222. package/client/components/terminal-info/base.jsx +25 -0
  223. package/client/components/terminal-info/content.jsx +101 -0
  224. package/client/components/terminal-info/data-cols-parser.jsx +50 -0
  225. package/client/components/terminal-info/disk.jsx +29 -0
  226. package/client/components/terminal-info/index.jsx +25 -0
  227. package/client/components/terminal-info/network.jsx +114 -0
  228. package/client/components/terminal-info/resource.jsx +80 -0
  229. package/client/components/terminal-info/run-cmd.jsx +273 -0
  230. package/client/components/terminal-info/terminal-info.styl +29 -0
  231. package/client/components/terminal-info/up.jsx +15 -0
  232. package/client/components/terminal-theme/index.jsx +264 -0
  233. package/client/components/terminal-theme/terminal-theme-list.styl +3 -0
  234. package/client/components/terminal-theme/theme-list.jsx +146 -0
  235. package/client/components/text-editor/text-editor-form.jsx +97 -0
  236. package/client/components/text-editor/text-editor.jsx +182 -0
  237. package/client/css/antd-overwrite.styl +14 -0
  238. package/client/css/basic.styl +38 -0
  239. package/client/css/includes/box.styl +154 -0
  240. package/client/css/includes/font-size.styl +6 -0
  241. package/client/css/includes/index.styl +3 -0
  242. package/client/css/includes/text.styl +31 -0
  243. package/client/css/includes/theme-default.styl +20 -0
  244. package/client/entry/basic.js +58 -0
  245. package/client/entry/index.jsx +15 -0
  246. package/client/entry/worker.js +137 -0
  247. package/client/store/address-bookmark.js +25 -0
  248. package/client/store/app-upgrade.js +23 -0
  249. package/client/store/batch-input-history.js +26 -0
  250. package/client/store/bookmark-group.js +128 -0
  251. package/client/store/bookmark.js +22 -0
  252. package/client/store/common.js +140 -0
  253. package/client/store/context-menu.js +23 -0
  254. package/client/store/db-upgrade.js +43 -0
  255. package/client/store/event.js +70 -0
  256. package/client/store/index.js +335 -0
  257. package/client/store/init-state.js +191 -0
  258. package/client/store/item.js +120 -0
  259. package/client/store/load-data.js +198 -0
  260. package/client/store/quick-command.js +43 -0
  261. package/client/store/session.js +54 -0
  262. package/client/store/setting.js +208 -0
  263. package/client/store/sidebar.js +48 -0
  264. package/client/store/sync.js +390 -0
  265. package/client/store/system-menu.js +120 -0
  266. package/client/store/tab.js +74 -0
  267. package/client/store/terminal-theme.js +116 -0
  268. package/client/store/transfer-history.js +27 -0
  269. package/client/store/transfer-list.js +20 -0
  270. package/client/store/ui-theme.js +71 -0
  271. package/client/store/watch.js +116 -0
  272. package/client/views/index.pug +58 -0
  273. package/package.json +34 -0
@@ -0,0 +1,1358 @@
1
+ import { Component } from 'react'
2
+ import ZmodemTransfer from './zmodem-transfer'
3
+ import { handleErr } from '../../common/fetch'
4
+ import generate from '../../common/uid'
5
+ import { isEqual, pick, debounce } from 'lodash-es'
6
+ import postMessage from '../../common/post-msg'
7
+ import clone from '../../common/to-simple-obj'
8
+ import runIdle from '../../common/run-idle'
9
+ import {
10
+ CheckCircleOutlined,
11
+ ReloadOutlined
12
+ } from '@ant-design/icons'
13
+
14
+ import {
15
+ notification,
16
+ Spin,
17
+ Modal,
18
+ Button,
19
+ Checkbox
20
+ } from 'antd'
21
+ import Input from '../common/input-auto-focus'
22
+ import classnames from 'classnames'
23
+ import './terminal.styl'
24
+ import {
25
+ statusMap,
26
+ paneMap,
27
+ typeMap,
28
+ isWin,
29
+ isMac,
30
+ terminalSshConfigType,
31
+ transferTypeMap,
32
+ terminalActions,
33
+ commonActions,
34
+ rendererTypes,
35
+ termInitId
36
+ } from '../../common/constants'
37
+ import deepCopy from 'json-deep-copy'
38
+ import { readClipboard, copy } from '../../common/clipboard'
39
+ import { FitAddon } from 'xterm-addon-fit'
40
+ import AttachAddon from './attach-addon-custom'
41
+ import { SearchAddon } from 'xterm-addon-search'
42
+ import { WebLinksAddon } from 'xterm-addon-web-links'
43
+ import { SerializeAddon } from 'xterm-addon-serialize'
44
+ import { CanvasAddon } from 'xterm-addon-canvas'
45
+ import { WebglAddon } from 'xterm-addon-webgl'
46
+ import getProxy from '../../common/get-proxy'
47
+ import { Zmodem, AddonZmodem } from './xterm-zmodem'
48
+ import { Unicode11Addon } from 'xterm-addon-unicode11'
49
+ import keyControlPressed from '../../common/key-control-pressed'
50
+ import keyShiftPressed from '../../common/key-shift-pressed'
51
+ import keyPressed from '../../common/key-pressed'
52
+ import { Terminal } from 'xterm'
53
+ import * as ls from '../../common/safe-local-storage'
54
+ import NormalBuffer from './normal-buffer'
55
+ import { createTerm, resizeTerm } from './terminal-apis'
56
+ import createLsId from './build-ls-term-id'
57
+
58
+ const { prefix } = window
59
+ const e = prefix('ssh')
60
+ const m = prefix('menu')
61
+ const f = prefix('form')
62
+ const c = prefix('common')
63
+ const authFailMsg = 'All configured authentication methods failed'
64
+ const privateKeyMsg = 'no passphrase'
65
+
66
+ const computePos = (e) => {
67
+ return {
68
+ left: e.clientX,
69
+ top: e.clientY
70
+ }
71
+ }
72
+
73
+ export default class Term extends Component {
74
+ constructor (props) {
75
+ super(props)
76
+ this.state = {
77
+ pid: '',
78
+ id: props.id || 'id' + generate(),
79
+ loading: false,
80
+ promoteModalVisible: false,
81
+ savePassword: false,
82
+ tempPassword: '',
83
+ passType: 'password',
84
+ zmodemTransfer: null,
85
+ lines: []
86
+ }
87
+ }
88
+
89
+ componentDidMount () {
90
+ this.initTerminal()
91
+ this.initEvt()
92
+ if (this.props.tab.enableSsh === false) {
93
+ ;(
94
+ document.querySelector('.session-current .term-sftp-tabs .type-tab.sftp') ||
95
+ document.querySelector('.session-current .term-sftp-tabs .type-tab.fileManager')
96
+ ).click()
97
+ }
98
+ }
99
+
100
+ componentDidUpdate (prevProps) {
101
+ const shouldChange = (
102
+ prevProps.currentTabId !== this.props.currentTabId &&
103
+ this.props.tab.id === this.props.currentTabId &&
104
+ this.props.pane === paneMap.terminal
105
+ ) || (
106
+ this.props.pane !== prevProps.pane &&
107
+ this.props.pane === paneMap.terminal
108
+ )
109
+ const names = [
110
+ 'width',
111
+ 'height',
112
+ 'left',
113
+ 'top'
114
+ ]
115
+ if (
116
+ !isEqual(
117
+ pick(this.props, names),
118
+ pick(prevProps, names)
119
+ ) ||
120
+ shouldChange
121
+ ) {
122
+ this.onResize()
123
+ }
124
+ if (shouldChange) {
125
+ this.term.focus()
126
+ }
127
+ this.checkConfigChange(
128
+ prevProps,
129
+ this.props
130
+ )
131
+ const themeChanged = !isEqual(
132
+ this.props.themeConfig,
133
+ prevProps.themeConfig
134
+ )
135
+ if (themeChanged) {
136
+ this.term.options.theme = deepCopy(this.props.themeConfig)
137
+ }
138
+ }
139
+
140
+ componentWillUnmount () {
141
+ Object.keys(this.timers).forEach(k => {
142
+ clearTimeout(this.timers[k])
143
+ })
144
+ this.onClose = true
145
+ this.socket && this.socket.close()
146
+ if (this.term) {
147
+ this.term.dispose()
148
+ }
149
+ window.removeEventListener(
150
+ 'resize',
151
+ this.onResize
152
+ )
153
+ window.removeEventListener('message', this.handleEvent)
154
+ this.dom.removeEventListener('contextmenu', this.onContextMenu)
155
+ window.removeEventListener('message', this.onContextAction)
156
+ }
157
+
158
+ terminalConfigProps = [
159
+ {
160
+ name: 'rightClickSelectsWord',
161
+ type: 'glob'
162
+ },
163
+ {
164
+ name: 'fontSize',
165
+ type: 'glob_local'
166
+ },
167
+ {
168
+ name: 'fontFamily',
169
+ type: 'glob_local'
170
+ }
171
+ ]
172
+
173
+ getValue = (props, type, name) => {
174
+ return type === 'glob'
175
+ ? props.config[name]
176
+ : props.tab[name] || props.config[name]
177
+ }
178
+
179
+ checkConfigChange = (prevProps, props) => {
180
+ for (const k of this.terminalConfigProps) {
181
+ const { name, type } = k
182
+ const prev = this.getValue(prevProps, type, name)
183
+ const curr = this.getValue(props, type, name)
184
+ if (
185
+ prev !== curr
186
+ ) {
187
+ this.term.options[name] = curr
188
+ if (['fontFamily', 'fontSize'].includes(name)) {
189
+ this.onResize()
190
+ }
191
+ }
192
+ }
193
+ }
194
+
195
+ timers = {}
196
+
197
+ initEvt = () => {
198
+ const { id } = this.state
199
+ const dom = document.getElementById(id)
200
+ this.dom = dom
201
+ dom.addEventListener('contextmenu', this.onContextMenu)
202
+ window.addEventListener(
203
+ 'resize',
204
+ this.onResize
205
+ )
206
+ window.addEventListener('message', this.handleEvent)
207
+ }
208
+
209
+ zoom = (v) => {
210
+ const { term } = this
211
+ if (!term) {
212
+ return
213
+ }
214
+ term.options.fontSize = term.options.fontSize + v
215
+ window.store.triggerResize()
216
+ }
217
+
218
+ isActiveTerminal = () => {
219
+ return this.props.id === this.props.activeSplitId &&
220
+ this.props.tab.id === this.props.currentTabId &&
221
+ this.props.pane === paneMap.terminal
222
+ }
223
+
224
+ handleEvent = (e) => {
225
+ const {
226
+ keyword,
227
+ options,
228
+ action,
229
+ encode,
230
+ id,
231
+ type,
232
+ cmd,
233
+ activeSplitId,
234
+ toAll,
235
+ inputOnly,
236
+ zoomValue
237
+ } = e?.data || {}
238
+ const { id: propSplitId } = this.props
239
+ if (
240
+ action === terminalActions.zoom &&
241
+ propSplitId === activeSplitId
242
+ ) {
243
+ this.zoom(zoomValue)
244
+ } else if (
245
+ action === terminalActions.changeEncode &&
246
+ propSplitId === activeSplitId
247
+ ) {
248
+ this.switchEncoding(encode)
249
+ } else if (
250
+ action === terminalActions.batchInput &&
251
+ (
252
+ toAll || propSplitId === activeSplitId
253
+ )
254
+ ) {
255
+ this.batchInput(cmd)
256
+ } else if (
257
+ action === terminalActions.showInfoPanel &&
258
+ (
259
+ propSplitId === activeSplitId
260
+ )
261
+ ) {
262
+ this.handleShowInfo()
263
+ } else if (
264
+ action === terminalActions.quickCommand &&
265
+ (
266
+ propSplitId === activeSplitId
267
+ )
268
+ ) {
269
+ e.stopPropagation()
270
+ this.term && this.attachAddon._sendData(
271
+ cmd +
272
+ (inputOnly ? '' : '\r')
273
+ )
274
+ this.term.focus()
275
+ } else if (
276
+ action === terminalActions.openTerminalSearch &&
277
+ (
278
+ propSplitId === activeSplitId
279
+ )
280
+ ) {
281
+ this.toggleSearch()
282
+ } else if (
283
+ action === terminalActions.doSearchNext &&
284
+ (
285
+ propSplitId === activeSplitId
286
+ )
287
+ ) {
288
+ this.searchNext(keyword, options)
289
+ } else if (
290
+ action === terminalActions.doSearchPrev &&
291
+ (
292
+ propSplitId === activeSplitId
293
+ )
294
+ ) {
295
+ this.searchPrev(keyword, options)
296
+ }
297
+ const isActiveTerminal = this.isActiveTerminal()
298
+ if (
299
+ type === 'focus' &&
300
+ isActiveTerminal
301
+ ) {
302
+ e.stopPropagation()
303
+ window.store.termFocused = true
304
+ return this.term && this.term.focus()
305
+ }
306
+ if (
307
+ type === 'blur' &&
308
+ isActiveTerminal
309
+ ) {
310
+ e.stopPropagation()
311
+ return this.term && this.term.blur()
312
+ }
313
+ if (
314
+ keyControlPressed(e) &&
315
+ !keyShiftPressed(e) &&
316
+ keyPressed(e, 'c')
317
+ ) {
318
+ const sel = this.term.getSelection()
319
+ if (sel) {
320
+ e.stopPropagation()
321
+ e.preventDefault()
322
+ this.copySelectionToClipboard()
323
+ return false
324
+ }
325
+ } else if (
326
+ keyControlPressed(e) &&
327
+ keyShiftPressed(e) &&
328
+ keyPressed(e, 'c')
329
+ ) {
330
+ e.stopPropagation()
331
+ this.copySelectionToClipboard()
332
+ } else if (id === this.props.id) {
333
+ e.stopPropagation()
334
+ this.term.selectAll()
335
+ } else if (
336
+ keyPressed(e, 'f') && keyControlPressed(e) &&
337
+ (
338
+ isMac ||
339
+ (!isMac && keyShiftPressed(e))
340
+ )
341
+ ) {
342
+ e.stopPropagation()
343
+ this.toggleSearch()
344
+ } else if (
345
+ keyPressed(e, 'tab')
346
+ ) {
347
+ e.stopPropagation()
348
+ e.preventDefault()
349
+ if (e.ctrlKey && e.type === 'keydown') {
350
+ if (e.shiftKey) {
351
+ window.store.clickPrevTab()
352
+ } else {
353
+ window.store.clickNextTab()
354
+ }
355
+ return false
356
+ }
357
+ } else if (
358
+ keyControlPressed(e) &&
359
+ keyPressed(e, 'ArrowUp') && this.bufferMode === 'alternate'
360
+ ) {
361
+ e.stopPropagation()
362
+ this.openNormalBuffer()
363
+ } else if (
364
+ e.ctrlKey &&
365
+ keyPressed(e, 'tab')
366
+ ) {
367
+ this.onClear()
368
+ } else if (
369
+ e.altKey &&
370
+ keyPressed(e, 'insert')
371
+ ) {
372
+ this.tryInsertSelected()
373
+ }
374
+ }
375
+
376
+ onDrop = e => {
377
+ const files = e?.dataTransfer?.files
378
+ if (files && files.length) {
379
+ this.attachAddon._sendData(
380
+ Array.from(files).map(f => `"${f.path}"`).join(' ')
381
+ )
382
+ }
383
+ }
384
+
385
+ onSelection = () => {
386
+ if (
387
+ !this.props.config.copyWhenSelect ||
388
+ window.store.onOperation
389
+ ) {
390
+ return false
391
+ }
392
+ this.copySelectionToClipboard()
393
+ }
394
+
395
+ copySelectionToClipboard = () => {
396
+ const txt = this.term.getSelection()
397
+ if (txt) {
398
+ copy(txt)
399
+ }
400
+ }
401
+
402
+ tryInsertSelected = () => {
403
+ const txt = this.term.getSelection()
404
+ if (txt) {
405
+ this.attachAddon._sendData(txt)
406
+ }
407
+ }
408
+
409
+ webLinkHandler = (event, url) => {
410
+ if (!this.props.config.ctrlOrMetaOpenTerminalLink) {
411
+ return window.openLink(url, '_blank')
412
+ }
413
+ if (keyControlPressed(event)) {
414
+ window.openLink(url, '_blank')
415
+ }
416
+ }
417
+
418
+ onzmodemRetract = () => {
419
+ log.debug('zmodemRetract')
420
+ }
421
+
422
+ onReceiveZmodemSession = () => {
423
+ // * zmodem transfer
424
+ // * then run rz to send from your browser or
425
+ // * sz <file> to send from the remote peer.
426
+ this.term.write('\r\nRecommmend use trzsz instead: https://github.com/trzsz/trzsz\r\n')
427
+ this.zsession.on('offer', this.onOfferReceive)
428
+ this.zsession.start()
429
+ return new Promise((resolve) => {
430
+ this.zsession.on('session_end', resolve)
431
+ }).then(this.onZmodemEnd).catch(this.onZmodemCatch)
432
+ }
433
+
434
+ updateProgress = (xfer, type) => {
435
+ if (this.onCanceling) {
436
+ return
437
+ }
438
+ const fileInfo = xfer.get_details()
439
+ const {
440
+ size
441
+ } = fileInfo
442
+ const total = xfer.get_offset() || 0
443
+ let percent = Math.floor(100 * total / size)
444
+ if (percent > 99) {
445
+ percent = 99
446
+ }
447
+ this.setState({
448
+ zmodemTransfer: {
449
+ fileInfo,
450
+ percent,
451
+ transferedSize: total,
452
+ type
453
+ }
454
+ })
455
+ }
456
+
457
+ saveToDisk = (xfer, buffer) => {
458
+ return Zmodem.Browser
459
+ .save_to_disk(buffer, xfer.get_details().name)
460
+ }
461
+
462
+ onOfferReceive = xfer => {
463
+ this.updateProgress(xfer, transferTypeMap.download)
464
+ const FILE_BUFFER = []
465
+ xfer.on('input', (payload) => {
466
+ this.updateProgress(xfer, transferTypeMap.download)
467
+ FILE_BUFFER.push(new Uint8Array(payload))
468
+ })
469
+ xfer.accept()
470
+ .then(
471
+ () => {
472
+ this.saveToDisk(xfer, FILE_BUFFER)
473
+ }
474
+ )
475
+ .catch(window.store.onError)
476
+ }
477
+
478
+ beforeZmodemUpload = (file, files) => {
479
+ if (!files.length) {
480
+ return false
481
+ }
482
+ // const f = files[0]
483
+ // if (f.size > maxZmodemUploadSize) {
484
+ // if (this.zsession) {
485
+ // this.zsession.abort()
486
+ // }
487
+ // this.onZmodemEnd()
488
+ // // if (this.props.tab.enableSftp) {
489
+ // // notification.info({
490
+ // // message: `Uploading by sftp`,
491
+ // // duration: 8
492
+ // // })
493
+ // // return this.transferBySftp(files)
494
+ // // } else {
495
+ // const url = 'https://github.com/FGasper/zmodemjs/issues/11'
496
+ // const msg = (
497
+ // <div>
498
+ // <p>Currently <b>rz</b> only support upload file size less than {filesize(maxZmodemUploadSize)}, due to known issue:</p>
499
+ // <p><Link to={url}>{url}</Link></p>
500
+ // <p>You can try upload in sftp which is much faster.</p>
501
+ // </div>
502
+ // )
503
+ // notification.error({
504
+ // message: msg,
505
+ // duration: 8
506
+ // })
507
+ // // }
508
+ // }
509
+ const th = this
510
+ Zmodem.Browser.send_files(
511
+ this.zsession,
512
+ files, {
513
+ on_offer_response (obj, xfer) {
514
+ if (xfer) {
515
+ th.updateProgress(xfer, transferTypeMap.upload)
516
+ }
517
+ },
518
+ on_progress (obj, xfer) {
519
+ th.updateProgress(xfer, transferTypeMap.upload)
520
+ }
521
+ }
522
+ )
523
+ .then(th.onZmodemEndSend)
524
+ .catch(th.onZmodemCatch)
525
+
526
+ return false
527
+ }
528
+
529
+ onSendZmodemSession = () => {
530
+ this.setState(() => {
531
+ return {
532
+ zmodemTransfer: {
533
+ type: transferTypeMap.upload
534
+ }
535
+ }
536
+ })
537
+ }
538
+
539
+ cancelZmodem = () => {
540
+ this.onZmodemEndSend()
541
+ }
542
+
543
+ onZmodemEndSend = () => {
544
+ this.zsession && this.zsession.close && this.zsession.close()
545
+ this.onZmodemEnd()
546
+ }
547
+
548
+ onZmodemEnd = () => {
549
+ delete this.onZmodem
550
+ this.onCanceling = true
551
+ this.attachAddon = new AttachAddon(
552
+ this.socket,
553
+ undefined,
554
+ this.props.tab.encode,
555
+ isWin && !this.isRemote()
556
+ )
557
+ if (this.decoder) {
558
+ this.attachAddon.decoder = this.decode
559
+ }
560
+ this.term.loadAddon(this.attachAddon)
561
+ this.setState(() => {
562
+ return {
563
+ zmodemTransfer: null
564
+ }
565
+ })
566
+ this.term.focus()
567
+ this.term.write('\r\n')
568
+ }
569
+
570
+ onZmodemCatch = (e) => {
571
+ window.store.onError(e)
572
+ this.onZmodemEnd()
573
+ }
574
+
575
+ onZmodemDetect = detection => {
576
+ this.onCanceling = false
577
+ this.attachAddon.dispose()
578
+ this.term.blur()
579
+ this.onZmodem = true
580
+ const zsession = detection.confirm()
581
+ this.zsession = zsession
582
+ if (zsession.type === 'receive') {
583
+ this.onReceiveZmodemSession()
584
+ } else {
585
+ this.onSendZmodemSession()
586
+ }
587
+ }
588
+
589
+ split = () => {
590
+ this.props.handleSplit(null, this.props.id)
591
+ }
592
+
593
+ onContextAction = e => {
594
+ const {
595
+ action,
596
+ id,
597
+ args = [],
598
+ func
599
+ } = e.data || {}
600
+ if (
601
+ action !== commonActions.clickContextMenu ||
602
+ id !== this.uid ||
603
+ !this[func]
604
+ ) {
605
+ return false
606
+ }
607
+ window.removeEventListener('message', this.onContextAction)
608
+ this[func](...args)
609
+ }
610
+
611
+ onContextMenu = e => {
612
+ e.preventDefault()
613
+ if (this.state.loading) {
614
+ return
615
+ }
616
+ if (this.props.config.pasteWhenContextMenu) {
617
+ return this.onPaste()
618
+ }
619
+ const items = this.renderContext()
620
+ this.uid = generate()
621
+ window.store.openContextMenu({
622
+ id: this.uid,
623
+ items,
624
+ pos: computePos(e)
625
+ })
626
+ window.addEventListener('message', this.onContextAction)
627
+ }
628
+
629
+ onCopy = () => {
630
+ const selected = this.term.getSelection()
631
+ copy(selected)
632
+ this.term.focus()
633
+ }
634
+
635
+ onSelectAll = () => {
636
+ this.term.selectAll()
637
+ }
638
+
639
+ onClear = () => {
640
+ this.term.clear()
641
+ this.term.focus()
642
+ this.notifyOnData('')
643
+ }
644
+
645
+ isRemote = () => {
646
+ return this.props.tab?.host &&
647
+ this.props.tab?.type !== terminalSshConfigType
648
+ }
649
+
650
+ onPaste = () => {
651
+ let selected = readClipboard()
652
+ if (isWin && this.isRemote()) {
653
+ selected = selected.replace(/\r\n/g, '\n')
654
+ }
655
+ this.term.paste(selected)
656
+ this.term.focus()
657
+ }
658
+
659
+ toggleSearch = () => {
660
+ window.store.toggleTerminalSearch()
661
+ }
662
+
663
+ onLineFeed = e => {
664
+ // console.log(e, 'onLineFeed')
665
+ }
666
+
667
+ onTitleChange = e => {
668
+ log.debug(e, 'title change')
669
+ }
670
+
671
+ onSearchResultsChange = ({ resultIndex, resultCount }) => {
672
+ window.store.storeAssign({
673
+ termSearchMatchCount: resultCount,
674
+ termSearchMatchIndex: resultIndex
675
+ })
676
+ }
677
+
678
+ searchPrev = (searchInput, options) => {
679
+ this.searchAddon.findPrevious(
680
+ searchInput, options
681
+ )
682
+ }
683
+
684
+ searchNext = (searchInput, options) => {
685
+ console.log(this.searchAddon.onDidChangeResults, 'searchAddon')
686
+ this.searchAddon.findNext(
687
+ searchInput, options
688
+ )
689
+ }
690
+
691
+ renderContext = () => {
692
+ const hasSlected = this.term.hasSelection()
693
+ const copyed = readClipboard()
694
+ const copyShortcut = isMac
695
+ ? 'Command+C'
696
+ : 'Ctrl+Shift+C'
697
+ const pasteShortcut = isMac
698
+ ? 'Command+V'
699
+ : 'Ctrl+Shift+V'
700
+ return [
701
+ {
702
+ func: 'onCopy',
703
+ icon: 'CopyOutlined',
704
+ text: m('copy'),
705
+ disabled: !hasSlected,
706
+ subText: copyShortcut
707
+ },
708
+ {
709
+ func: 'onPaste',
710
+ icon: 'SwitcherOutlined',
711
+ text: m('paste'),
712
+ disabled: !copyed,
713
+ subText: pasteShortcut
714
+ },
715
+ {
716
+ func: 'onClear',
717
+ icon: 'ReloadOutlined',
718
+ text: e('clear'),
719
+ subText: 'Ctrl+L'
720
+ },
721
+ {
722
+ func: 'onSelectAll',
723
+ icon: 'SelectOutlined',
724
+ text: e('selectAll')
725
+ },
726
+ {
727
+ func: 'toggleSearch',
728
+ icon: 'SearchOutlined',
729
+ text: e('search')
730
+ },
731
+ {
732
+ func: 'split',
733
+ icon: 'BorderHorizontalOutlined',
734
+ text: e('split')
735
+ }
736
+ ]
737
+ }
738
+
739
+ loadState = term => {
740
+ const uid = this.props.stateId || this.state.id
741
+ if (uid === termInitId) {
742
+ const id = createLsId(this.props.stateId || this.state.id)
743
+ let str = ls.getItem(id)
744
+ const after = '\r\n\r\n=======\r\nload from history\r\n=======\r\n\r\n'
745
+ str = str.replace(/\s+=======\s+load from history\s+=======\s+/g, '\r\n').trim()
746
+ if (str) {
747
+ term.write(str + after)
748
+ }
749
+ }
750
+ }
751
+
752
+ notifyOnData = debounce(() => {
753
+ const str = this.serializeAddon.serialize()
754
+ const id = createLsId(this.state.id)
755
+ ls.setItem(id, str)
756
+ postMessage({
757
+ action: 'terminal-receive-data',
758
+ tabId: this.props.tab.id
759
+ })
760
+ }, 100)
761
+
762
+ onSocketData = () => {
763
+ if (this.state.id === termInitId) {
764
+ runIdle(this.notifyOnData)
765
+ }
766
+ }
767
+
768
+ loadRenderer = (term, config) => {
769
+ if (config.rendererType === rendererTypes.canvas) {
770
+ term.loadAddon(new CanvasAddon())
771
+ } else if (config.rendererType === rendererTypes.webGL) {
772
+ try {
773
+ term.loadAddon(new WebglAddon())
774
+ } catch (e) {
775
+ log.error('render with webgl failed, fallback to canvas')
776
+ log.error(e)
777
+ term.loadAddon(new CanvasAddon())
778
+ }
779
+ }
780
+ }
781
+
782
+ initTerminal = async () => {
783
+ const { id } = this.state
784
+ // let {password, privateKey, host} = this.props.tab
785
+ const { themeConfig, tab = {}, config = {} } = this.props
786
+ const term = new Terminal({
787
+ allowProposedApi: true,
788
+ scrollback: config.scrollback,
789
+ rightClickSelectsWord: config.rightClickSelectsWord || false,
790
+ fontFamily: tab.fontFamily || config.fontFamily,
791
+ theme: themeConfig,
792
+ allowTransparency: true,
793
+ // lineHeight: 1.2,
794
+ cursorStyle: config.cursorStyle,
795
+ cursorBlink: config.cursorBlink,
796
+ fontSize: tab.fontSize || config.fontSize,
797
+ screenReaderMode: config.screenReaderMode
798
+ })
799
+ this.fitAddon = new FitAddon()
800
+ this.searchAddon = new SearchAddon()
801
+ this.searchAddon.onDidChangeResults(this.onSearchResultsChange)
802
+ const unicode11Addon = new Unicode11Addon()
803
+ this.serializeAddon = new SerializeAddon()
804
+ term.loadAddon(this.serializeAddon)
805
+ term.loadAddon(unicode11Addon)
806
+ // activate the new version
807
+ term.unicode.activeVersion = '11'
808
+ term.loadAddon(this.fitAddon)
809
+ term.loadAddon(this.searchAddon)
810
+ term.onLineFeed(this.onLineFeed)
811
+ term.onTitleChange(this.onTitleChange)
812
+ term.onSelectionChange(this.onSelection)
813
+ this.loadState(term)
814
+ term.open(document.getElementById(id), true)
815
+ this.loadRenderer(term, config)
816
+ term.textarea.addEventListener('focus', this.setActive)
817
+ // term.textarea.addEventListener('blur', this.onBlur)
818
+
819
+ // term.on('keydown', this.handleEvent)
820
+ this.term = term
821
+ term.attachCustomKeyEventHandler(this.handleEvent)
822
+ term.onKey(this.onKey)
823
+ // if (host && !password && !privateKey) {
824
+ // return this.promote()
825
+ // }
826
+ await this.remoteInit(term)
827
+ }
828
+
829
+ setActive = () => {
830
+ this.props.setActive(this.props.id)
831
+ window.store.storeAssign({
832
+ activeTerminalId: this.props.id
833
+ })
834
+ }
835
+
836
+ runInitScript = () => {
837
+ const {
838
+ type,
839
+ title,
840
+ startDirectory,
841
+ runScripts
842
+ } = this.props.tab
843
+ if (type === terminalSshConfigType) {
844
+ const cmd = `ssh ${title.split(/\s/g)[0]}\r`
845
+ return this.attachAddon._sendData(cmd)
846
+ }
847
+ if (startDirectory) {
848
+ const cmd = `cd ${startDirectory}\r`
849
+ this.attachAddon._sendData(cmd)
850
+ }
851
+ if (runScripts && runScripts.length) {
852
+ this.delayedScripts = deepCopy(runScripts)
853
+ this.timers.timerDelay = setTimeout(this.runDelayedScripts, this.delayedScripts[0].delay || 0)
854
+ }
855
+ }
856
+
857
+ runDelayedScripts = () => {
858
+ const { delayedScripts } = this
859
+ if (delayedScripts && delayedScripts.length > 0) {
860
+ const obj = delayedScripts.shift()
861
+ if (obj.script) {
862
+ this.attachAddon._sendData(obj.script + '\r')
863
+ }
864
+ if (delayedScripts.length > 0) {
865
+ this.timers.timerDelay = setTimeout(this.runDelayedScripts, this.delayedScripts[0].delay || 0)
866
+ }
867
+ }
868
+ }
869
+
870
+ count = 0
871
+
872
+ setStatus = status => {
873
+ const id = this.props.tab?.id
874
+ this.props.editTab(id, {
875
+ status
876
+ })
877
+ }
878
+
879
+ watchNormalBufferTrigger = e => {
880
+ if (
881
+ keyControlPressed(e) &&
882
+ keyPressed(e, 'ArrowUp')
883
+ ) {
884
+ e.stopPropagation()
885
+ this.copySelectionToClipboard()
886
+ }
887
+ }
888
+
889
+ openNormalBuffer = () => {
890
+ const normal = this.term.buffer._normal
891
+ const len = normal.length
892
+ const lines = new Array(len).fill('').map((x, i) => {
893
+ return normal.getLine(i).translateToString(false)
894
+ })
895
+ this.setState({
896
+ lines
897
+ })
898
+ }
899
+
900
+ closeNormalBuffer = () => {
901
+ this.setState({
902
+ lines: []
903
+ })
904
+ this.term.focus()
905
+ }
906
+
907
+ onBufferChange = buf => {
908
+ this.bufferMode = buf.type
909
+ }
910
+
911
+ remoteInit = async (term = this.term) => {
912
+ this.setState({
913
+ loading: true
914
+ })
915
+ const { cols, rows } = term
916
+ const { config } = this.props
917
+ const { host, port, tokenElecterm } = config
918
+ const { sessionId, terminalIndex, id, logName } = this.props
919
+ const tab = deepCopy(this.props.tab || {})
920
+ const {
921
+ srcId, from = 'bookmarks',
922
+ type,
923
+ encode,
924
+ term: terminalType
925
+ } = tab
926
+ const { savePassword } = this.state
927
+ const isSshConfig = type === terminalSshConfigType
928
+ const termType = isSshConfig
929
+ ? typeMap.local
930
+ : type
931
+ const extra = this.props.sessionOptions
932
+ const opts = clone({
933
+ cols,
934
+ rows,
935
+ term: terminalType || config.terminalType,
936
+ saveTerminalLogToFile: config.saveTerminalLogToFile,
937
+ ...tab,
938
+ ...extra,
939
+ logName,
940
+ ...pick(config, [
941
+ 'keepaliveInterval',
942
+ 'keepaliveCountMax',
943
+ 'execWindows',
944
+ 'execMac',
945
+ 'execLinux',
946
+ 'execWindowsArgs',
947
+ 'execMacArgs',
948
+ 'execLinuxArgs',
949
+ 'debug'
950
+ ]),
951
+ sessionId,
952
+ tabId: id,
953
+ terminalIndex,
954
+ termType,
955
+ readyTimeout: config.sshReadyTimeout,
956
+ proxy: getProxy(tab, config),
957
+ type: tab.host && !isSshConfig
958
+ ? typeMap.remote
959
+ : typeMap.local
960
+ })
961
+ delete opts.terminals
962
+ let pid = await createTerm(opts)
963
+ .catch(err => {
964
+ const text = err.message
965
+ if (text.includes(authFailMsg)) {
966
+ this.setState(() => ({ passType: 'password' }))
967
+ return 'fail'
968
+ } else if (text.includes(privateKeyMsg)) {
969
+ this.setState(() => ({ passType: 'passphrase' }))
970
+ return 'fail-private'
971
+ } else {
972
+ handleErr({ message: text })
973
+ }
974
+ })
975
+ pid = pid || ''
976
+ if (pid.includes('fail')) {
977
+ return this.promote()
978
+ }
979
+ if (savePassword) {
980
+ window.store.editItem(srcId, extra, from)
981
+ }
982
+ this.setState({
983
+ loading: false
984
+ })
985
+ if (!pid) {
986
+ this.setStatus(statusMap.error)
987
+ return
988
+ }
989
+ this.setStatus(statusMap.success)
990
+ this.props.setSessionState({
991
+ pid
992
+ })
993
+ term.pid = pid
994
+ this.pid = pid
995
+ this.setState({
996
+ pid
997
+ })
998
+ const wsUrl = `ws://${host}:${port}/terminals/${pid}?sessionId=${sessionId}&token=${tokenElecterm}`
999
+ const socket = new WebSocket(wsUrl)
1000
+ socket.onclose = this.oncloseSocket
1001
+ socket.onerror = this.onerrorSocket
1002
+ this.attachAddon = new AttachAddon(
1003
+ socket,
1004
+ undefined,
1005
+ encode,
1006
+ isWin && !this.isRemote()
1007
+ )
1008
+ term.loadAddon(this.attachAddon)
1009
+ socket.onopen = () => {
1010
+ socket.addEventListener('message', this.onSocketData)
1011
+ term._initialized = true
1012
+ }
1013
+ this.socket = socket
1014
+ // term.onRrefresh(this.onRefresh)
1015
+ term.onResize(this.onResizeTerminal)
1016
+ if (pick(term, 'buffer._onBufferChange._listeners')) {
1017
+ term.buffer._onBufferChange._listeners.push(this.onBufferChange)
1018
+ }
1019
+ const cid = this.props.currentTabId
1020
+ const tid = this.props.tab.id
1021
+ if (cid === tid && this.props.tab.status === statusMap.success) {
1022
+ term.loadAddon(new WebLinksAddon(this.webLinkHandler))
1023
+ term.focus()
1024
+ this.zmodemAddon = new AddonZmodem()
1025
+ this.fitAddon.fit()
1026
+ term.loadAddon(this.zmodemAddon)
1027
+ term.zmodemAttach(this.socket, {
1028
+ noTerminalWriteOutsideSession: true
1029
+ }, this)
1030
+ }
1031
+
1032
+ // this.decoder = new TextDecoder(encode)
1033
+ // const oldWrite = term.write
1034
+ // const th = this
1035
+ // term.write = function (data) {
1036
+ // let str = ''
1037
+ // if (typeof data === 'object') {
1038
+ // if (data instanceof ArrayBuffer) {
1039
+ // str = th.decoder.decode(data)
1040
+ // oldWrite.call(term, str)
1041
+ // } else {
1042
+ // const fileReader = new FileReader()
1043
+ // fileReader.addEventListener('load', () => {
1044
+ // str = th.decoder.decode(fileReader.result)
1045
+ // oldWrite.call(term, str)
1046
+ // })
1047
+ // fileReader.readAsArrayBuffer(new window.Blob([data]))
1048
+ // }
1049
+ // } else if (typeof data === 'string') {
1050
+ // oldWrite.call(term, data)
1051
+ // } else {
1052
+ // throw Error(`Cannot handle ${typeof data} websocket message.`)
1053
+ // }
1054
+ // }
1055
+ this.term = term
1056
+ this.runInitScript()
1057
+ window.store.triggerResize()
1058
+ }
1059
+
1060
+ onKey = (key, e) => {
1061
+ // log.log('onKey', key, e)
1062
+ }
1063
+
1064
+ onResize = debounce(() => {
1065
+ const cid = this.props.currentTabId
1066
+ const tid = this.props.tab?.id
1067
+ if (
1068
+ this.props.tab.status === statusMap.success &&
1069
+ cid === tid &&
1070
+ this.term
1071
+ ) {
1072
+ try {
1073
+ this.fitAddon.fit()
1074
+ } catch (e) {
1075
+ log.info('resize failed')
1076
+ }
1077
+ }
1078
+ }, 200)
1079
+
1080
+ onerrorSocket = err => {
1081
+ this.setStatus(statusMap.error)
1082
+ log.warning('onerrorSocket', err)
1083
+ }
1084
+
1085
+ oncloseSocket = () => {
1086
+ if (this.onClose) {
1087
+ return
1088
+ }
1089
+ this.setStatus(
1090
+ statusMap.error
1091
+ )
1092
+ if (!this.isActiveTerminal() || !window.focused) {
1093
+ return false
1094
+ }
1095
+ const key = `open${Date.now()}`
1096
+ function closeMsg () {
1097
+ notification.destroy(key)
1098
+ }
1099
+ this.socketCloseWarning = notification.warning({
1100
+ key,
1101
+ message: e('socketCloseTip'),
1102
+ description: (
1103
+ <div className='pd2y'>
1104
+ <Button
1105
+ className='mg1r'
1106
+ type='primary'
1107
+ onClick={() => {
1108
+ closeMsg()
1109
+ this.props.delSplit(this.state.id)
1110
+ }}
1111
+ >
1112
+ {m('close')}
1113
+ </Button>
1114
+ <Button
1115
+ icon={<ReloadOutlined />}
1116
+ onClick={() => {
1117
+ closeMsg()
1118
+ this.props.reloadTab(
1119
+ this.props.tab
1120
+ )
1121
+ }}
1122
+ >
1123
+ {m('reload')}
1124
+ </Button>
1125
+ </div>
1126
+ )
1127
+ })
1128
+ }
1129
+
1130
+ batchInput = (cmd) => {
1131
+ this.attachAddon._sendData(cmd + '\r')
1132
+ }
1133
+
1134
+ onResizeTerminal = size => {
1135
+ const { cols, rows } = size
1136
+ resizeTerm(this.pid, this.props.sessionId, cols, rows)
1137
+ }
1138
+
1139
+ promote = () => {
1140
+ this.setState({
1141
+ promoteModalVisible: true,
1142
+ tempPassword: ''
1143
+ })
1144
+ }
1145
+
1146
+ handleCancel = () => {
1147
+ const { id } = this.props.tab
1148
+ this.props.delTab(id)
1149
+ }
1150
+
1151
+ handleToggleSavePass = () => {
1152
+ this.setState({
1153
+ savePassword: !this.state.savePassword
1154
+ })
1155
+ }
1156
+
1157
+ renderPasswordForm = () => {
1158
+ const { tempPassword, savePassword, promoteModalVisible } = this.state
1159
+ const { type } = this.props.tab
1160
+ return (
1161
+ <div>
1162
+ <Input
1163
+ value={tempPassword}
1164
+ type='password'
1165
+ autofocustrigger={promoteModalVisible ? 1 : 2}
1166
+ selectall='yes'
1167
+ onChange={this.handleChangePass}
1168
+ onPressEnter={this.handleClickConfirmPass}
1169
+ />
1170
+ {
1171
+ type !== terminalSshConfigType
1172
+ ? (
1173
+ <div className='pd1t'>
1174
+ <Checkbox
1175
+ checked={savePassword}
1176
+ onChange={this.handleToggleSavePass}
1177
+ >{f('save')}
1178
+ </Checkbox>
1179
+ </div>
1180
+ )
1181
+ : null
1182
+ }
1183
+ </div>
1184
+ )
1185
+ }
1186
+
1187
+ handleChangePass = e => {
1188
+ this.setState({
1189
+ tempPassword: e.target.value
1190
+ })
1191
+ }
1192
+
1193
+ handleClickConfirmPass = () => {
1194
+ const {
1195
+ tempPassword,
1196
+ passType
1197
+ } = this.state
1198
+ this.props.setSessionState(old => {
1199
+ const sessionOptions = deepCopy(old.sessionOptions) || {}
1200
+ sessionOptions[passType] = tempPassword
1201
+ return { sessionOptions }
1202
+ })
1203
+ this.setState({
1204
+ promoteModalVisible: false
1205
+ }, this.remoteInit)
1206
+ }
1207
+
1208
+ handleShowInfo = () => {
1209
+ const { id, sessionId, logName } = this.props
1210
+ const { pid } = this.state
1211
+ const infoProps = {
1212
+ logName,
1213
+ id,
1214
+ pid,
1215
+ sessionId,
1216
+ isRemote: this.isRemote(),
1217
+ isActive: this.isActiveTerminal()
1218
+ }
1219
+ this.props.handleShowInfo(infoProps)
1220
+ }
1221
+
1222
+ // getPwd = async () => {
1223
+ // const { sessionId, config } = this.props
1224
+ // const { pid } = this.state
1225
+ // const prps = {
1226
+ // host: config.host,
1227
+ // port: config.port,
1228
+ // pid,
1229
+ // sessionId
1230
+ // }
1231
+ // const result = await runCmds(prps, ['pwd'])
1232
+ // .catch(window.store.onError)
1233
+ // return result ? result[0].trim() : ''
1234
+ // }
1235
+
1236
+ renderPromoteModal = () => {
1237
+ if (!this.isActiveTerminal()) {
1238
+ return null
1239
+ }
1240
+ const {
1241
+ passType = 'password'
1242
+ } = this.state
1243
+ const props = {
1244
+ title: f(passType) + '?',
1245
+ content: this.renderPasswordForm(),
1246
+ onCancel: this.handleCancel,
1247
+ show: this.state.promoteModalVisible,
1248
+ footer: this.renderModalFooter(),
1249
+ cancelText: c('cancel')
1250
+ }
1251
+ return (
1252
+ <Modal
1253
+ {...props}
1254
+ >
1255
+ {this.renderPasswordForm()}
1256
+ </Modal>
1257
+ )
1258
+ }
1259
+
1260
+ renderModalFooter = () => {
1261
+ const disabled = !this.state.tempPassword
1262
+ return (
1263
+ <div className='alignright pd1'>
1264
+ <Button
1265
+ type='primary'
1266
+ icon={<CheckCircleOutlined />}
1267
+ disabled={disabled}
1268
+ onClick={this.handleClickConfirmPass}
1269
+ className='mg1r'
1270
+ >
1271
+ {c('ok')}
1272
+ </Button>
1273
+ <Button
1274
+ type='dashed'
1275
+ className='mg1r'
1276
+ onClick={this.handleCancel}
1277
+ >
1278
+ {c('cancel')}
1279
+ </Button>
1280
+ </div>
1281
+ )
1282
+ }
1283
+
1284
+ switchEncoding = encode => {
1285
+ this.decode = new TextDecoder(encode)
1286
+ this.attachAddon.decoder = this.decode
1287
+ }
1288
+
1289
+ render () {
1290
+ const { id, loading, zmodemTransfer } = this.state
1291
+ const { height, width, left, top, position, id: pid, activeSplitId } = this.props
1292
+ const cls = classnames('term-wrap', {
1293
+ 'not-first-term': !!position
1294
+ }, 'tw-' + pid, {
1295
+ 'terminal-not-active': activeSplitId !== pid
1296
+ })
1297
+ const prps1 = {
1298
+ className: cls,
1299
+ style: {
1300
+ height,
1301
+ width,
1302
+ left,
1303
+ top,
1304
+ zIndex: position / 10
1305
+ },
1306
+ onDrop: this.onDrop
1307
+ }
1308
+ // const fileProps = {
1309
+ // type: 'file',
1310
+ // multiple: true,
1311
+ // id: `${id}-file-sel`,
1312
+ // className: 'hide'
1313
+ // }
1314
+ const prps2 = {
1315
+ className: 'absolute term-wrap-1',
1316
+ style: {
1317
+ left: '10px',
1318
+ top: '10px',
1319
+ right: 0,
1320
+ bottom: 0
1321
+ }
1322
+ }
1323
+ const prps3 = {
1324
+ id,
1325
+ className: 'absolute term-wrap-2',
1326
+ style: {
1327
+ left: 0,
1328
+ top: 0,
1329
+ height: '100%',
1330
+ width: '100%'
1331
+ }
1332
+ }
1333
+ return (
1334
+ <div
1335
+ {...prps1}
1336
+ >
1337
+ {this.renderPromoteModal()}
1338
+ <div
1339
+ {...prps2}
1340
+ >
1341
+ <div
1342
+ {...prps3}
1343
+ />
1344
+ <NormalBuffer
1345
+ lines={this.state.lines}
1346
+ close={this.closeNormalBuffer}
1347
+ />
1348
+ </div>
1349
+ <ZmodemTransfer
1350
+ zmodemTransfer={zmodemTransfer}
1351
+ cancelZmodem={this.cancelZmodem}
1352
+ beforeZmodemUpload={this.beforeZmodemUpload}
1353
+ />
1354
+ <Spin className='loading-wrapper' spinning={loading} />
1355
+ </div>
1356
+ )
1357
+ }
1358
+ }