@electerm/electerm-react 2.3.198 → 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/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 +2 -1
- 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/terminal-command-dropdown.jsx +3 -0
- package/client/components/terminal/terminal.jsx +165 -103
- 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 -50
- 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,39 @@
|
|
|
1
1
|
export class KeywordHighlighterAddon {
|
|
2
2
|
constructor (keywords) {
|
|
3
|
-
this.keywords = keywords
|
|
3
|
+
this.keywords = keywords || []
|
|
4
|
+
this.compiledPatterns = this.compilePatterns()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// Pre-compile all regex patterns once for better performance
|
|
8
|
+
compilePatterns = () => {
|
|
9
|
+
const patterns = []
|
|
10
|
+
for (const obj of this.keywords) {
|
|
11
|
+
const { keyword, color = 'red' } = obj || {}
|
|
12
|
+
if (keyword) {
|
|
13
|
+
try {
|
|
14
|
+
patterns.push({
|
|
15
|
+
regex: new RegExp(keyword, 'gi'),
|
|
16
|
+
colorCode: this.getColorCode(color)
|
|
17
|
+
})
|
|
18
|
+
} catch (e) {
|
|
19
|
+
console.error('Invalid keyword regex:', keyword, e)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return patterns
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
getColorCode = (color) => {
|
|
27
|
+
const colorMap = {
|
|
28
|
+
green: '\u001b[32m',
|
|
29
|
+
yellow: '\u001b[33m',
|
|
30
|
+
blue: '\u001b[34m',
|
|
31
|
+
magenta: '\u001b[35m',
|
|
32
|
+
cyan: '\u001b[36m',
|
|
33
|
+
white: '\u001b[37m',
|
|
34
|
+
red: '\u001b[31m'
|
|
35
|
+
}
|
|
36
|
+
return colorMap[color] || colorMap.red
|
|
4
37
|
}
|
|
5
38
|
|
|
6
39
|
escape = str => {
|
|
@@ -8,63 +41,71 @@ export class KeywordHighlighterAddon {
|
|
|
8
41
|
.replace(/\033/g, '\\033')
|
|
9
42
|
}
|
|
10
43
|
|
|
11
|
-
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
return '\u001b[32m$&\u001b[0m'
|
|
16
|
-
case 'yellow':
|
|
17
|
-
return '\u001b[33m$&\u001b[0m'
|
|
18
|
-
case 'blue':
|
|
19
|
-
return '\u001b[34m$&\u001b[0m'
|
|
20
|
-
case 'magenta':
|
|
21
|
-
return '\u001b[35m$&\u001b[0m'
|
|
22
|
-
case 'cyan':
|
|
23
|
-
return '\u001b[36m$&\u001b[0m'
|
|
24
|
-
case 'white':
|
|
25
|
-
return '\u001b[37m$&\u001b[0m'
|
|
26
|
-
default:
|
|
27
|
-
return '\u001b[31m$&\u001b[0m'
|
|
44
|
+
highlightKeywords = (text) => {
|
|
45
|
+
// Early exit if no patterns
|
|
46
|
+
if (this.compiledPatterns.length === 0) {
|
|
47
|
+
return text
|
|
28
48
|
}
|
|
29
|
-
}
|
|
30
49
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
// Split text into segments: ANSI/OSC sequences vs plain text
|
|
51
|
+
// Match OSC sequences (ESC ] ... BEL or ESC ] ... ESC \) and CSI sequences (ESC [ ... letter)
|
|
52
|
+
// Use String.fromCharCode to avoid lint warnings about control characters
|
|
53
|
+
const ESC = String.fromCharCode(27) // \x1b
|
|
54
|
+
const BEL = String.fromCharCode(7) // \x07
|
|
55
|
+
// eslint-disable-next-line no-control-regex
|
|
56
|
+
const ansiPattern = new RegExp('(' + ESC + '\\][^' + BEL + ESC + ']*(?:' + BEL + '|' + ESC + '\\\\)|' + ESC + '\\[[0-9;]*[A-Za-z])', 'g')
|
|
57
|
+
|
|
58
|
+
const segments = []
|
|
59
|
+
let lastIndex = 0
|
|
60
|
+
let match
|
|
61
|
+
|
|
62
|
+
while ((match = ansiPattern.exec(text)) !== null) {
|
|
63
|
+
// Add plain text before this sequence
|
|
64
|
+
if (match.index > lastIndex) {
|
|
65
|
+
segments.push({ type: 'text', content: text.slice(lastIndex, match.index) })
|
|
46
66
|
}
|
|
67
|
+
// Add the ANSI sequence (don't highlight)
|
|
68
|
+
segments.push({ type: 'ansi', content: match[0] })
|
|
69
|
+
lastIndex = ansiPattern.lastIndex
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Add remaining plain text
|
|
73
|
+
if (lastIndex < text.length) {
|
|
74
|
+
segments.push({ type: 'text', content: text.slice(lastIndex) })
|
|
47
75
|
}
|
|
48
|
-
|
|
76
|
+
|
|
77
|
+
// Highlight only plain text segments
|
|
78
|
+
const result = segments.map(seg => {
|
|
79
|
+
if (seg.type === 'ansi') {
|
|
80
|
+
return seg.content
|
|
81
|
+
}
|
|
82
|
+
let content = seg.content
|
|
83
|
+
for (const { regex, colorCode } of this.compiledPatterns) {
|
|
84
|
+
regex.lastIndex = 0
|
|
85
|
+
content = content.replace(regex, (m) => `${colorCode}${m}\u001b[0m`)
|
|
86
|
+
}
|
|
87
|
+
return content
|
|
88
|
+
}).join('')
|
|
89
|
+
|
|
90
|
+
return result
|
|
49
91
|
}
|
|
50
92
|
|
|
51
93
|
activate (terminal) {
|
|
52
94
|
this.terminal = terminal
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
terminal.displayRaw ?
|
|
95
|
+
// Store the original write method properly bound to terminal
|
|
96
|
+
this.originalWrite = terminal.write.bind(terminal)
|
|
97
|
+
const self = this
|
|
98
|
+
terminal.write = function (data) {
|
|
99
|
+
self.originalWrite(
|
|
100
|
+
terminal.displayRaw ? self.escape(data) : self.highlightKeywords(data)
|
|
59
101
|
)
|
|
60
102
|
}
|
|
61
|
-
this.originalWrite = originalWrite
|
|
62
103
|
}
|
|
63
104
|
|
|
64
105
|
dispose () {
|
|
65
106
|
// Restore the original write method when disposing the addon
|
|
66
107
|
this.terminal.write = this.originalWrite
|
|
67
108
|
this.originalWrite = null
|
|
68
|
-
this.
|
|
109
|
+
this.terminal = null
|
|
69
110
|
}
|
|
70
111
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side Shell Integration Commands
|
|
3
|
+
*
|
|
4
|
+
* These are minimal shell integration commands that can be sent directly
|
|
5
|
+
* to a local or remote shell from the frontend after connection.
|
|
6
|
+
* They enable OSC 633 command tracking without needing server-side file sourcing.
|
|
7
|
+
*
|
|
8
|
+
* OSC 633 Protocol:
|
|
9
|
+
* - OSC 633 ; A - Prompt started
|
|
10
|
+
* - OSC 633 ; B - Command input started (ready for typing)
|
|
11
|
+
* - OSC 633 ; C - Command execution started
|
|
12
|
+
* - OSC 633 ; D ; <exitCode> - Command finished
|
|
13
|
+
* - OSC 633 ; E ; <command> - Command line being executed
|
|
14
|
+
* - OSC 633 ; P ; Cwd=<path> - Current working directory
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/* eslint-disable no-template-curly-in-string, no-useless-escape */
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get inline shell integration command for bash (one-liner format)
|
|
21
|
+
* Properly formatted for semicolon joining
|
|
22
|
+
*/
|
|
23
|
+
function getBashInlineIntegration () {
|
|
24
|
+
// Each statement is complete and can be joined with semicolons
|
|
25
|
+
return [
|
|
26
|
+
'if [[ $- == *i* ]] && [[ -z "${ELECTERM_SHELL_INTEGRATION:-}" ]]',
|
|
27
|
+
'then export ELECTERM_SHELL_INTEGRATION=1',
|
|
28
|
+
'__e_esc() { local v="$1"; v="${v//\\\\/\\\\\\\\}"; v="${v//;/\\\\x3b}"; printf \'%s\' "$v"; }',
|
|
29
|
+
'__e_pre() { [[ "$BASH_COMMAND" == "$PROMPT_COMMAND" ]] && return; [[ "$BASH_COMMAND" == "__e_"* ]] && return; [[ "${__e_in:-0}" == "0" ]] && { __e_in=1; printf \'\\e]633;E;%s\\a\\e]633;C\\a\' "$(__e_esc "$BASH_COMMAND")"; }; }',
|
|
30
|
+
'__e_cmd() { local c="$?"; [[ "${__e_in:-0}" == "1" ]] && { printf \'\\e]633;D;%s\\a\' "$c"; __e_in=0; }; printf \'\\e]633;P;Cwd=%s\\a\\e]633;A\\a\' "$(__e_esc "$PWD")"; return "$c"; }',
|
|
31
|
+
'trap \'__e_pre\' DEBUG',
|
|
32
|
+
'PROMPT_COMMAND="__e_cmd${PROMPT_COMMAND:+; $PROMPT_COMMAND}"',
|
|
33
|
+
'fi'
|
|
34
|
+
].join('; ')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get inline shell integration command for zsh (one-liner format)
|
|
39
|
+
* Properly formatted for semicolon joining
|
|
40
|
+
*/
|
|
41
|
+
function getZshInlineIntegration () {
|
|
42
|
+
// Each statement is complete and can be joined with semicolons
|
|
43
|
+
// Note: 'then' must have a space/newline before the next command, not semicolon
|
|
44
|
+
return [
|
|
45
|
+
'if [[ -o interactive ]] && [[ -z "${ELECTERM_SHELL_INTEGRATION:-}" ]]',
|
|
46
|
+
'then export ELECTERM_SHELL_INTEGRATION=1',
|
|
47
|
+
'__e_esc() { local v="$1"; v="${v//\\\\/\\\\\\\\}"; v="${v//;/\\\\x3b}"; builtin printf \'%s\' "$v"; }',
|
|
48
|
+
'__e_preexec() { __e_cmd="$1"; builtin printf \'\\e]633;E;%s\\a\\e]633;C\\a\' "$(__e_esc "$1")"; }',
|
|
49
|
+
'__e_precmd() { local c="$?"; [[ -n "$__e_cmd" ]] && builtin printf \'\\e]633;D;%s\\a\' "$c"; __e_cmd=""; builtin printf \'\\e]633;P;Cwd=%s\\a\\e]633;A\\a\' "$(__e_esc "$PWD")"; }',
|
|
50
|
+
'autoload -Uz add-zsh-hook',
|
|
51
|
+
'add-zsh-hook precmd __e_precmd',
|
|
52
|
+
'add-zsh-hook preexec __e_preexec',
|
|
53
|
+
'fi'
|
|
54
|
+
].join('; ')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get inline shell integration command for fish (one-liner format)
|
|
59
|
+
*/
|
|
60
|
+
function getFishInlineIntegration () {
|
|
61
|
+
return [
|
|
62
|
+
'if status is-interactive; and not set -q ELECTERM_SHELL_INTEGRATION',
|
|
63
|
+
'set -g ELECTERM_SHELL_INTEGRATION 1',
|
|
64
|
+
'function __e_esc; echo $argv | string replace -a \'\\\\\' \'\\\\\\\\\' | string replace -a \';\' \'\\\\x3b\'; end',
|
|
65
|
+
'function __e_prompt --on-event fish_prompt; printf \'\\e]633;A\\a\\e]633;P;Cwd=%s\\a\' (__e_esc "$PWD"); end',
|
|
66
|
+
'function __e_preexec --on-event fish_preexec; printf \'\\e]633;E;%s\\a\\e]633;C\\a\' (__e_esc "$argv"); end',
|
|
67
|
+
'function __e_postexec --on-event fish_postexec; printf \'\\e]633;D;%s\\a\' $status; end',
|
|
68
|
+
'end'
|
|
69
|
+
].join('; ')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get shell integration command based on detected shell type
|
|
74
|
+
* @param {string} shellType - 'bash', 'zsh', or 'fish'
|
|
75
|
+
* @returns {string} Shell integration command to send
|
|
76
|
+
*/
|
|
77
|
+
export function getInlineShellIntegration (shellType) {
|
|
78
|
+
switch (shellType) {
|
|
79
|
+
case 'bash':
|
|
80
|
+
return getBashInlineIntegration()
|
|
81
|
+
case 'zsh':
|
|
82
|
+
return getZshInlineIntegration()
|
|
83
|
+
case 'fish':
|
|
84
|
+
return getFishInlineIntegration()
|
|
85
|
+
default:
|
|
86
|
+
// Try bash as default for sh-compatible shells
|
|
87
|
+
return getBashInlineIntegration()
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Wrap shell integration command for execution
|
|
93
|
+
* Now simplified since output suppression is handled at the attach addon level
|
|
94
|
+
* @param {string} cmd - Shell integration command
|
|
95
|
+
* @param {string} shellType - Shell type (unused, kept for API compatibility)
|
|
96
|
+
* @returns {string} Command ready to send to terminal
|
|
97
|
+
*/
|
|
98
|
+
export function wrapSilent (cmd, shellType) {
|
|
99
|
+
// Escape single quotes for embedding in single-quoted string
|
|
100
|
+
const escaped = cmd.replace(/'/g, "'\\''")
|
|
101
|
+
// The leading space prevents the command from being saved to history
|
|
102
|
+
// The eval wrapper ensures proper execution
|
|
103
|
+
return ` eval '${escaped}' 2>/dev/null\r`
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get complete shell integration command ready to send
|
|
108
|
+
* @param {string} shellType - 'bash', 'zsh', or 'fish'
|
|
109
|
+
* @returns {string} Complete command to send to terminal
|
|
110
|
+
*/
|
|
111
|
+
export function getShellIntegrationCommand (shellType = 'bash') {
|
|
112
|
+
const cmd = getInlineShellIntegration(shellType)
|
|
113
|
+
return wrapSilent(cmd, shellType)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Detect shell type from shell path or login script
|
|
118
|
+
* @param {string} shellPath - Path to shell executable or login script
|
|
119
|
+
* @returns {string} Shell type: 'bash', 'zsh', 'fish', or 'bash' (default)
|
|
120
|
+
*/
|
|
121
|
+
export function detectShellType (shellPath = '') {
|
|
122
|
+
if (!shellPath) return 'bash'
|
|
123
|
+
|
|
124
|
+
const normalizedPath = shellPath.toLowerCase()
|
|
125
|
+
|
|
126
|
+
if (normalizedPath.includes('zsh')) {
|
|
127
|
+
return 'zsh'
|
|
128
|
+
} else if (normalizedPath.includes('fish')) {
|
|
129
|
+
return 'fish'
|
|
130
|
+
} else if (normalizedPath.includes('bash')) {
|
|
131
|
+
return 'bash'
|
|
132
|
+
} else if (normalizedPath.includes('sh')) {
|
|
133
|
+
// Generic sh, try bash compatibility
|
|
134
|
+
return 'bash'
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return 'bash'
|
|
138
|
+
}
|
|
@@ -171,6 +171,7 @@ export default class TerminalCmdSuggestions extends Component {
|
|
|
171
171
|
const { activeTabId } = window.store
|
|
172
172
|
const terminal = refs.get('term-' + activeTabId)
|
|
173
173
|
if (!terminal) {
|
|
174
|
+
console.log('No active terminal found')
|
|
174
175
|
return
|
|
175
176
|
}
|
|
176
177
|
|
|
@@ -186,6 +187,8 @@ export default class TerminalCmdSuggestions extends Component {
|
|
|
186
187
|
txt = pre + command
|
|
187
188
|
}
|
|
188
189
|
terminal.attachAddon._sendData(txt)
|
|
190
|
+
// Update the terminal's currentInput to reflect the full command
|
|
191
|
+
terminal.setCurrentInput(command)
|
|
189
192
|
terminal.term.focus()
|
|
190
193
|
this.closeSuggestions()
|
|
191
194
|
}
|