@electerm/electerm-react 2.3.191 → 2.4.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.
- package/client/common/clipboard.js +1 -1
- package/client/common/constants.js +2 -2
- package/client/common/download.jsx +3 -2
- package/client/common/error-handler.jsx +5 -9
- package/client/common/fetch-from-server.js +1 -1
- package/client/common/fetch.jsx +5 -5
- package/client/common/icon-helpers.jsx +16 -0
- package/client/common/parse-json-safe.js +1 -1
- package/client/common/pre.js +0 -7
- package/client/common/sftp.js +1 -1
- package/client/common/terminal-theme.js +1 -1
- package/client/common/transfer.js +2 -2
- package/client/common/upgrade.js +2 -2
- package/client/components/ai/ai-chat.jsx +10 -1
- package/client/components/auth/login.jsx +1 -1
- package/client/components/bg/css-overwrite.jsx +1 -1
- package/client/components/bookmark-form/common/fields.jsx +3 -0
- package/client/components/bookmark-form/common/ssh-agent.jsx +33 -0
- package/client/components/bookmark-form/config/common-fields.js +2 -4
- package/client/components/bookmark-form/config/serial.js +1 -1
- package/client/components/bookmark-form/config/ssh.js +1 -0
- package/client/components/bookmark-form/form-renderer.jsx +3 -2
- package/client/components/common/input-auto-focus.jsx +1 -1
- package/client/components/common/message.jsx +131 -0
- package/client/components/common/message.styl +58 -0
- package/client/components/common/modal.jsx +176 -0
- package/client/components/common/modal.styl +22 -0
- package/client/components/common/notification-with-details.jsx +1 -1
- package/client/components/common/notification.jsx +94 -0
- package/client/components/common/notification.styl +51 -0
- package/client/components/main/connection-hopping-warnning.jsx +1 -3
- package/client/components/main/error-wrapper.jsx +3 -2
- package/client/components/main/main.jsx +4 -11
- package/client/components/main/upgrade.jsx +6 -4
- package/client/components/profile/profile-form-elem.jsx +1 -1
- package/client/components/quick-commands/quick-commands-box.jsx +5 -2
- package/client/components/quick-commands/quick-commands-form-elem.jsx +1 -1
- package/client/components/rdp/rdp-session.jsx +2 -2
- package/client/components/session/session.jsx +4 -9
- package/client/components/setting-panel/deep-link-control.jsx +4 -3
- package/client/components/setting-panel/keyword-input.jsx +60 -0
- package/client/components/setting-panel/keywords-form.jsx +2 -7
- package/client/components/setting-panel/setting-common.jsx +1 -1
- package/client/components/setting-panel/setting-terminal.jsx +1 -1
- package/client/components/setting-panel/tab-settings.jsx +1 -1
- package/client/components/setting-sync/setting-sync-form.jsx +53 -3
- package/client/components/setting-sync/setting-sync.jsx +2 -1
- package/client/components/sftp/owner-list.js +6 -6
- package/client/components/sftp/sftp-entry.jsx +6 -4
- package/client/components/shortcuts/shortcut-editor.jsx +2 -2
- package/client/components/ssh-config/ssh-config-load-notify.jsx +3 -2
- package/client/components/tabs/tab.jsx +1 -1
- package/client/components/tabs/workspace-save-modal.jsx +2 -1
- package/client/components/terminal/attach-addon-custom.js +142 -26
- package/client/components/terminal/command-tracker-addon.js +164 -53
- package/client/components/terminal/highlight-addon.js +84 -43
- package/client/components/terminal/shell.js +138 -0
- package/client/components/terminal/term-search.styl +1 -0
- package/client/components/terminal/terminal-command-dropdown.jsx +3 -0
- package/client/components/terminal/terminal.jsx +166 -104
- package/client/components/theme/theme-form.jsx +2 -1
- package/client/components/tree-list/bookmark-transport.jsx +27 -5
- package/client/components/vnc/vnc-session.jsx +1 -1
- package/client/components/widgets/widget-notification-with-details.jsx +1 -1
- package/client/store/common.js +5 -2
- package/client/store/db-upgrade.js +1 -1
- package/client/store/init-state.js +2 -1
- package/client/store/load-data.js +2 -2
- package/client/store/mcp-handler.js +9 -56
- package/client/store/setting.js +1 -3
- package/client/store/store.js +2 -1
- package/client/store/sync.js +14 -8
- package/client/store/system-menu.js +2 -1
- package/client/store/tab.js +1 -1
- package/client/store/widgets.js +1 -3
- package/package.json +1 -1
- package/client/common/track.js +0 -7
- package/client/components/batch-op/batch-op-entry.jsx +0 -13
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Form,
|
|
3
|
-
Input,
|
|
4
3
|
Select,
|
|
5
4
|
Space,
|
|
6
5
|
Button
|
|
@@ -11,6 +10,7 @@ import {
|
|
|
11
10
|
PlusOutlined
|
|
12
11
|
} from '@ant-design/icons'
|
|
13
12
|
import { useEffect } from 'react'
|
|
13
|
+
import KeywordInput from './keyword-input'
|
|
14
14
|
|
|
15
15
|
const FormItem = Form.Item
|
|
16
16
|
const FormList = Form.List
|
|
@@ -26,10 +26,6 @@ export default function KeywordForm (props) {
|
|
|
26
26
|
formChild.submit()
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
function handleChange (e) {
|
|
30
|
-
formChild.submit()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
29
|
function handleFinish (data) {
|
|
34
30
|
props.submit(data)
|
|
35
31
|
}
|
|
@@ -59,9 +55,8 @@ export default function KeywordForm (props) {
|
|
|
59
55
|
name={[field.name, 'keyword']}
|
|
60
56
|
rules={[{ validator: checker }]}
|
|
61
57
|
>
|
|
62
|
-
<
|
|
58
|
+
<KeywordInput
|
|
63
59
|
addonBefore={renderBefore(field.name)}
|
|
64
|
-
onChange={handleChange}
|
|
65
60
|
/>
|
|
66
61
|
</FormItem>
|
|
67
62
|
</FormItem>
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { useDelta, useConditionalEffect } from 'react-delta-hooks'
|
|
9
9
|
import { ArrowDownOutlined, ArrowUpOutlined, SaveOutlined, ClearOutlined } from '@ant-design/icons'
|
|
10
|
-
import { Button, Input,
|
|
10
|
+
import { Button, Input, Form, Alert } from 'antd'
|
|
11
|
+
import { notification } from '../common/notification'
|
|
11
12
|
import Link from '../common/external-link'
|
|
12
13
|
import dayjs from 'dayjs'
|
|
13
14
|
import eq from 'fast-deep-equal'
|
|
@@ -15,6 +16,7 @@ import { syncTokenCreateUrls, syncTypes } from '../../common/constants'
|
|
|
15
16
|
import HelpIcon from '../common/help-icon'
|
|
16
17
|
import ServerDataStatus from './server-data-status'
|
|
17
18
|
import Password from '../common/password'
|
|
19
|
+
import { isError } from 'lodash-es'
|
|
18
20
|
|
|
19
21
|
const FormItem = Form.Item
|
|
20
22
|
const e = window.translate
|
|
@@ -58,11 +60,17 @@ export default function SyncForm (props) {
|
|
|
58
60
|
up[syncType + 'ApiUrl'] = 'https://electerm-cloud.html5beta.com/api/sync'
|
|
59
61
|
// up[syncType + 'ApiUrl'] = 'http://127.0.0.1:5678/api/sync'
|
|
60
62
|
}
|
|
63
|
+
if (res.proxy) {
|
|
64
|
+
up[syncType + 'Proxy'] = res.proxy
|
|
65
|
+
} else {
|
|
66
|
+
up[syncType + 'Proxy'] = ''
|
|
67
|
+
}
|
|
61
68
|
window.store.updateSyncSetting(up)
|
|
62
69
|
const test = await window.store.testSyncToken(syncType, res.gistId)
|
|
63
|
-
if (
|
|
70
|
+
if (isError(test)) {
|
|
64
71
|
return notification.error({
|
|
65
|
-
|
|
72
|
+
message: test.message || 'Request failed',
|
|
73
|
+
description: test.stack || 'Request failed'
|
|
66
74
|
})
|
|
67
75
|
}
|
|
68
76
|
if (!res.gistId && syncType !== syncTypes.custom && syncType !== syncTypes.cloud) {
|
|
@@ -136,6 +144,27 @@ export default function SyncForm (props) {
|
|
|
136
144
|
function createId (name) {
|
|
137
145
|
return 'sync-input-' + name + '-' + syncType
|
|
138
146
|
}
|
|
147
|
+
function renderWarning () {
|
|
148
|
+
if (syncType === syncTypes.gitee) {
|
|
149
|
+
return (
|
|
150
|
+
<Alert
|
|
151
|
+
message={
|
|
152
|
+
<span>
|
|
153
|
+
Gitee data sync is not recommended. For more information, please refer to the
|
|
154
|
+
<Link to='https://github.com/electerm/electerm/wiki/gitee-data-sync-warning' className='mg1l'>
|
|
155
|
+
wiki
|
|
156
|
+
</Link>
|
|
157
|
+
.
|
|
158
|
+
</span>
|
|
159
|
+
}
|
|
160
|
+
type='warning'
|
|
161
|
+
showIcon
|
|
162
|
+
className='mg1b'
|
|
163
|
+
/>
|
|
164
|
+
)
|
|
165
|
+
}
|
|
166
|
+
return null
|
|
167
|
+
}
|
|
139
168
|
function createUrlItem () {
|
|
140
169
|
if (syncType === syncTypes.cloud) {
|
|
141
170
|
return (
|
|
@@ -215,6 +244,23 @@ export default function SyncForm (props) {
|
|
|
215
244
|
</FormItem>
|
|
216
245
|
)
|
|
217
246
|
}
|
|
247
|
+
function createProxyItem () {
|
|
248
|
+
return (
|
|
249
|
+
<FormItem
|
|
250
|
+
label='Proxy'
|
|
251
|
+
name='proxy'
|
|
252
|
+
normalize={trim}
|
|
253
|
+
rules={[{
|
|
254
|
+
max: 200, message: '200 chars max'
|
|
255
|
+
}]}
|
|
256
|
+
>
|
|
257
|
+
<Input
|
|
258
|
+
placeholder='socks5://127.0.0.1:1080'
|
|
259
|
+
id={createId('proxy')}
|
|
260
|
+
/>
|
|
261
|
+
</FormItem>
|
|
262
|
+
)
|
|
263
|
+
}
|
|
218
264
|
const sprops = {
|
|
219
265
|
type: syncType,
|
|
220
266
|
status: props.serverStatus
|
|
@@ -228,6 +274,7 @@ export default function SyncForm (props) {
|
|
|
228
274
|
layout='vertical'
|
|
229
275
|
initialValues={props.formData}
|
|
230
276
|
>
|
|
277
|
+
{renderWarning()}
|
|
231
278
|
{createUrlItem()}
|
|
232
279
|
<FormItem
|
|
233
280
|
label={tokenLabel}
|
|
@@ -251,6 +298,9 @@ export default function SyncForm (props) {
|
|
|
251
298
|
{
|
|
252
299
|
createPasswordItem()
|
|
253
300
|
}
|
|
301
|
+
{
|
|
302
|
+
createProxyItem()
|
|
303
|
+
}
|
|
254
304
|
<FormItem>
|
|
255
305
|
<p>
|
|
256
306
|
<Button
|
|
@@ -43,7 +43,8 @@ export default auto(function SyncSettingEntry (props) {
|
|
|
43
43
|
url: syncSetting[type + 'Url'],
|
|
44
44
|
apiUrl: syncSetting[type + 'ApiUrl'],
|
|
45
45
|
lastSyncTime: syncSetting[type + 'LastSyncTime'],
|
|
46
|
-
syncPassword: syncSetting[type + 'SyncPassword']
|
|
46
|
+
syncPassword: syncSetting[type + 'SyncPassword'],
|
|
47
|
+
proxy: syncSetting[type + 'Proxy']
|
|
47
48
|
}
|
|
48
49
|
return (
|
|
49
50
|
<SyncForm
|
|
@@ -29,7 +29,7 @@ const linuxListGroup = 'cat /etc/group'
|
|
|
29
29
|
|
|
30
30
|
export async function remoteListUsers (pid) {
|
|
31
31
|
const users = await runCmd(pid, linuxListUser)
|
|
32
|
-
.catch(
|
|
32
|
+
.catch(console.error)
|
|
33
33
|
if (users) {
|
|
34
34
|
return parseNames(users)
|
|
35
35
|
}
|
|
@@ -38,7 +38,7 @@ export async function remoteListUsers (pid) {
|
|
|
38
38
|
|
|
39
39
|
export async function remoteListGroups (pid) {
|
|
40
40
|
const groups = await runCmd(pid, linuxListGroup)
|
|
41
|
-
.catch(
|
|
41
|
+
.catch(console.error)
|
|
42
42
|
if (groups) {
|
|
43
43
|
return parseNames(groups)
|
|
44
44
|
}
|
|
@@ -50,7 +50,7 @@ export async function localListUsers () {
|
|
|
50
50
|
return {}
|
|
51
51
|
} else if (isMac) {
|
|
52
52
|
const g = await fs.run('dscl . -list /Users UniqueID')
|
|
53
|
-
.catch(
|
|
53
|
+
.catch(console.error)
|
|
54
54
|
return g
|
|
55
55
|
? g.split('\n')
|
|
56
56
|
.reduce((p, s) => {
|
|
@@ -65,7 +65,7 @@ export async function localListUsers () {
|
|
|
65
65
|
}, {})
|
|
66
66
|
: {}
|
|
67
67
|
} else {
|
|
68
|
-
const g = await fs.run(linuxListUser).catch(
|
|
68
|
+
const g = await fs.run(linuxListUser).catch(console.error)
|
|
69
69
|
return g
|
|
70
70
|
? parseNames(g)
|
|
71
71
|
: {}
|
|
@@ -77,7 +77,7 @@ export async function localListGroups () {
|
|
|
77
77
|
return {}
|
|
78
78
|
} else if (isMac) {
|
|
79
79
|
const g = await fs.run('dscl . list /Groups PrimaryGroupID')
|
|
80
|
-
.catch(
|
|
80
|
+
.catch(console.error)
|
|
81
81
|
return g
|
|
82
82
|
? g.split('\n')
|
|
83
83
|
.reduce((p, s) => {
|
|
@@ -89,7 +89,7 @@ export async function localListGroups () {
|
|
|
89
89
|
}, {})
|
|
90
90
|
: {}
|
|
91
91
|
} else {
|
|
92
|
-
const g = await fs.run(linuxListGroup).catch(
|
|
92
|
+
const g = await fs.run(linuxListGroup).catch(console.error)
|
|
93
93
|
return g
|
|
94
94
|
? parseNames(g)
|
|
95
95
|
: {}
|
|
@@ -2,7 +2,9 @@ import { Component } from 'react'
|
|
|
2
2
|
import { refs } from '../common/ref'
|
|
3
3
|
import generate from '../../common/uid'
|
|
4
4
|
import runIdle from '../../common/run-idle'
|
|
5
|
-
import { Spin
|
|
5
|
+
import { Spin } from 'antd'
|
|
6
|
+
import { notification } from '../common/notification'
|
|
7
|
+
import Modal from '../common/modal'
|
|
6
8
|
import clone from '../../common/to-simple-obj'
|
|
7
9
|
import { isEqual, last, isNumber, some, isArray, pick, uniq, debounce } from 'lodash-es'
|
|
8
10
|
import FileSection from './file-item'
|
|
@@ -846,8 +848,8 @@ export default class Sftp extends Component {
|
|
|
846
848
|
sftp,
|
|
847
849
|
realpath
|
|
848
850
|
).catch(e => {
|
|
849
|
-
|
|
850
|
-
|
|
851
|
+
console.debug('seems a bad symbolic link')
|
|
852
|
+
console.debug(e)
|
|
851
853
|
return null
|
|
852
854
|
})
|
|
853
855
|
if (!realFileInfo) {
|
|
@@ -981,7 +983,7 @@ export default class Sftp extends Component {
|
|
|
981
983
|
const np = this.parsePath(type, this.state[nt])
|
|
982
984
|
if (!isValidPath(np)) {
|
|
983
985
|
return notification.warning({
|
|
984
|
-
|
|
986
|
+
message: 'path not valid'
|
|
985
987
|
})
|
|
986
988
|
}
|
|
987
989
|
this.setState({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useEffect } from 'react'
|
|
2
|
-
import {
|
|
2
|
+
import { Button } from 'antd'
|
|
3
|
+
import { notification } from '../common/notification'
|
|
3
4
|
import * as ls from '../../common/safe-local-storage'
|
|
4
5
|
import {
|
|
5
6
|
sshConfigKey,
|
|
@@ -20,7 +21,7 @@ function handleIgnore () {
|
|
|
20
21
|
|
|
21
22
|
function showNotification () {
|
|
22
23
|
notification.info({
|
|
23
|
-
|
|
24
|
+
message: e('loadSshConfigs'),
|
|
24
25
|
duration: 0,
|
|
25
26
|
placement: 'bottom',
|
|
26
27
|
key: 'sshConfigNotify',
|
|
@@ -12,9 +12,9 @@ import {
|
|
|
12
12
|
} from '@ant-design/icons'
|
|
13
13
|
import {
|
|
14
14
|
Tooltip,
|
|
15
|
-
message,
|
|
16
15
|
Dropdown
|
|
17
16
|
} from 'antd'
|
|
17
|
+
import message from '../common/message'
|
|
18
18
|
import classnames from 'classnames'
|
|
19
19
|
import { pick } from 'lodash-es'
|
|
20
20
|
import Input from '../common/input-auto-focus'
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
import React, { useState } from 'react'
|
|
6
6
|
import { auto } from 'manate/react'
|
|
7
7
|
import Modal from '../common/modal'
|
|
8
|
-
import { Input, Select, Button, Space,
|
|
8
|
+
import { Input, Select, Button, Space, Radio } from 'antd'
|
|
9
|
+
import message from '../common/message'
|
|
9
10
|
import { SaveOutlined, EditOutlined } from '@ant-design/icons'
|
|
10
11
|
|
|
11
12
|
const e = window.translate
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* customize AttachAddon
|
|
3
3
|
*/
|
|
4
4
|
import { AttachAddon } from '@xterm/addon-attach'
|
|
5
|
-
import regEscape from 'escape-string-regexp'
|
|
6
5
|
|
|
7
6
|
export default class AttachAddonCustom extends AttachAddon {
|
|
8
7
|
constructor (term, socket, isWindowsShell) {
|
|
@@ -10,6 +9,80 @@ export default class AttachAddonCustom extends AttachAddon {
|
|
|
10
9
|
this.term = term
|
|
11
10
|
this.socket = socket
|
|
12
11
|
this.isWindowsShell = isWindowsShell
|
|
12
|
+
// Output suppression state for shell integration injection
|
|
13
|
+
this.outputSuppressed = false
|
|
14
|
+
this.suppressedData = []
|
|
15
|
+
this.suppressTimeout = null
|
|
16
|
+
this.onSuppressionEndCallback = null
|
|
17
|
+
// Track if we've received initial data from the terminal
|
|
18
|
+
this.hasReceivedInitialData = false
|
|
19
|
+
this.onInitialDataCallback = null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Set callback for when initial data is received
|
|
24
|
+
* @param {Function} callback - Called when first data arrives
|
|
25
|
+
*/
|
|
26
|
+
onInitialData = (callback) => {
|
|
27
|
+
if (this.hasReceivedInitialData) {
|
|
28
|
+
// Already received, call immediately
|
|
29
|
+
callback()
|
|
30
|
+
} else {
|
|
31
|
+
this.onInitialDataCallback = callback
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Start suppressing output - used during shell integration injection
|
|
37
|
+
* @param {number} timeout - Max time to suppress in ms (safety fallback)
|
|
38
|
+
* @param {Function} onEnd - Callback when suppression ends
|
|
39
|
+
*/
|
|
40
|
+
startOutputSuppression = (timeout = 3000, onEnd = null) => {
|
|
41
|
+
this.outputSuppressed = true
|
|
42
|
+
this.suppressedData = []
|
|
43
|
+
this.onSuppressionEndCallback = onEnd
|
|
44
|
+
// Safety timeout to ensure we always resume
|
|
45
|
+
this.suppressTimeout = setTimeout(() => {
|
|
46
|
+
console.warn('[AttachAddon] Output suppression timeout reached, resuming')
|
|
47
|
+
this.stopOutputSuppression(false)
|
|
48
|
+
}, timeout)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Stop suppressing output and optionally discard buffered data
|
|
53
|
+
* @param {boolean} discard - If true, discard buffered data; if false, write it to terminal
|
|
54
|
+
*/
|
|
55
|
+
stopOutputSuppression = (discard = true) => {
|
|
56
|
+
if (this.suppressTimeout) {
|
|
57
|
+
clearTimeout(this.suppressTimeout)
|
|
58
|
+
this.suppressTimeout = null
|
|
59
|
+
}
|
|
60
|
+
this.outputSuppressed = false
|
|
61
|
+
|
|
62
|
+
if (!discard && this.suppressedData.length > 0) {
|
|
63
|
+
// Write buffered data to terminal
|
|
64
|
+
for (const data of this.suppressedData) {
|
|
65
|
+
this.writeToTerminalDirect(data)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
this.suppressedData = []
|
|
69
|
+
|
|
70
|
+
// Call the end callback if set
|
|
71
|
+
if (this.onSuppressionEndCallback) {
|
|
72
|
+
const callback = this.onSuppressionEndCallback
|
|
73
|
+
this.onSuppressionEndCallback = null
|
|
74
|
+
callback()
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if we should resume output based on OSC 633 detection
|
|
80
|
+
* Called when shell integration is detected
|
|
81
|
+
*/
|
|
82
|
+
onShellIntegrationDetected = () => {
|
|
83
|
+
if (this.outputSuppressed) {
|
|
84
|
+
this.stopOutputSuppression(true) // Discard the integration command output
|
|
85
|
+
}
|
|
13
86
|
}
|
|
14
87
|
|
|
15
88
|
activate (terminal = this.term) {
|
|
@@ -43,11 +116,76 @@ export default class AttachAddonCustom extends AttachAddon {
|
|
|
43
116
|
}
|
|
44
117
|
}
|
|
45
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Check if data contains OSC 633 shell integration sequences
|
|
121
|
+
* @param {string} str - Data string to check
|
|
122
|
+
* @returns {boolean} True if OSC 633 sequence detected
|
|
123
|
+
*/
|
|
124
|
+
checkForShellIntegration = (str) => {
|
|
125
|
+
// OSC 633 sequences: ESC]633;X where X is A, B, C, D, E, or P
|
|
126
|
+
// ESC is character code 27 (0x1b)
|
|
127
|
+
// Use includes with the actual characters to avoid lint warning
|
|
128
|
+
const ESC = String.fromCharCode(27)
|
|
129
|
+
return str.includes(ESC + ']633;')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Write directly to terminal, bypassing suppression check
|
|
134
|
+
* Used for flushing buffered data
|
|
135
|
+
*/
|
|
136
|
+
writeToTerminalDirect = (data) => {
|
|
137
|
+
const { term } = this
|
|
138
|
+
if (term.parent?.onZmodem) {
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
if (typeof data === 'string') {
|
|
142
|
+
return term.write(data)
|
|
143
|
+
}
|
|
144
|
+
term?.write(data)
|
|
145
|
+
}
|
|
146
|
+
|
|
46
147
|
writeToTerminal = (data) => {
|
|
47
148
|
const { term } = this
|
|
48
149
|
if (term.parent?.onZmodem) {
|
|
49
150
|
return
|
|
50
151
|
}
|
|
152
|
+
|
|
153
|
+
// Track initial data arrival
|
|
154
|
+
if (!this.hasReceivedInitialData) {
|
|
155
|
+
this.hasReceivedInitialData = true
|
|
156
|
+
if (this.onInitialDataCallback) {
|
|
157
|
+
const callback = this.onInitialDataCallback
|
|
158
|
+
this.onInitialDataCallback = null
|
|
159
|
+
// Call after a micro-delay to ensure this data is written first
|
|
160
|
+
setTimeout(callback, 0)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Check for shell integration in the data (only when suppressing)
|
|
165
|
+
if (this.outputSuppressed) {
|
|
166
|
+
let str = data
|
|
167
|
+
if (typeof data !== 'string') {
|
|
168
|
+
// Convert to string to check for OSC 633
|
|
169
|
+
const decoder = this.decoder || new TextDecoder('utf-8')
|
|
170
|
+
try {
|
|
171
|
+
str = decoder.decode(data instanceof ArrayBuffer ? data : new Uint8Array(data))
|
|
172
|
+
} catch (e) {
|
|
173
|
+
str = ''
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// If we detect OSC 633, shell integration is working
|
|
178
|
+
if (this.checkForShellIntegration(str)) {
|
|
179
|
+
this.onShellIntegrationDetected()
|
|
180
|
+
// Don't buffer this - just discard the integration output
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Buffer the data while suppressed
|
|
185
|
+
this.suppressedData.push(data)
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
|
|
51
189
|
if (typeof data === 'string') {
|
|
52
190
|
return term.write(data)
|
|
53
191
|
}
|
|
@@ -62,31 +200,9 @@ export default class AttachAddonCustom extends AttachAddon {
|
|
|
62
200
|
const { term } = this
|
|
63
201
|
term?.parent?.notifyOnData()
|
|
64
202
|
const str = this.decoder.decode(data)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
} = term
|
|
69
|
-
const nss = str.split('\r')
|
|
70
|
-
const nnss = []
|
|
71
|
-
for (const str1 of nss) {
|
|
72
|
-
const ns = str1.trim()
|
|
73
|
-
if (cwdId) {
|
|
74
|
-
const cwdIdEscaped = regEscape(cwdId)
|
|
75
|
-
const dirRegex = new RegExp(`${cwdIdEscaped}([^\\n]+?)${cwdIdEscaped}`, 'g')
|
|
76
|
-
if (ns.match(dirRegex)) {
|
|
77
|
-
const cwd = dirRegex.exec(ns)[1].trim()
|
|
78
|
-
if (cwd === '~' || cwd === '%d' || cwd === '%/' || cwd === '$PWD') term.parent.setCwd('')
|
|
79
|
-
else term.parent.setCwd(cwd)
|
|
80
|
-
nnss.push(ns.replaceAll(dirRegex, ''))
|
|
81
|
-
} else nnss.push(str1)
|
|
82
|
-
} else {
|
|
83
|
-
nnss.push(str1)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
term.write(nnss.join('\r'))
|
|
87
|
-
} else {
|
|
88
|
-
term?.write(str)
|
|
89
|
-
}
|
|
203
|
+
// CWD tracking is now handled by shell integration automatically
|
|
204
|
+
// No need to parse PS1 markers
|
|
205
|
+
term?.write(str)
|
|
90
206
|
}
|
|
91
207
|
|
|
92
208
|
sendToServer = (data) => {
|