@electerm/electerm-react 5.3.26 → 5.5.9
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/README.md +246 -12
- package/client/common/choose-save-folder.js +21 -2
- package/client/common/constants.js +20 -6
- package/client/common/create-title.jsx +6 -0
- package/client/common/default-setting.js +15 -0
- package/client/common/init-setting-item.js +5 -0
- package/client/common/platform.js +24 -0
- package/client/components/ai/agent-tool-call-card.jsx +10 -2
- package/client/components/ai/agent.js +4 -1
- package/client/components/ai/ai-attachments.js +87 -0
- package/client/components/ai/ai-chat-empty.jsx +43 -0
- package/client/components/ai/ai-chat-entry.jsx +13 -4
- package/client/components/ai/ai-chat-history-item.jsx +28 -4
- package/client/components/ai/ai-chat-history.jsx +2 -1
- package/client/components/ai/ai-chat.jsx +104 -13
- package/client/components/ai/ai-config-props.js +4 -130
- package/client/components/ai/ai-config.jsx +135 -15
- package/client/components/ai/ai-presets.js +173 -0
- package/client/components/ai/ai.styl +83 -0
- package/client/components/bg/css-overwrite.jsx +11 -0
- package/client/components/bookmark-form/bookmark-schema.js +4 -0
- package/client/components/bookmark-form/common/fields.jsx +3 -0
- package/client/components/bookmark-form/config/common-fields.js +12 -0
- package/client/components/bookmark-form/config/local.js +3 -2
- package/client/components/bookmark-form/config/serial.js +3 -2
- package/client/components/bookmark-form/config/ssh.js +2 -1
- package/client/components/bookmark-form/config/telnet.js +3 -2
- package/client/components/common/item-filter.jsx +76 -0
- package/client/components/common/item-filter.styl +54 -0
- package/client/components/common/lazy-boundary.jsx +34 -0
- package/client/components/common/opacity.jsx +3 -1
- package/client/components/footer/cmd-history.jsx +132 -45
- package/client/components/footer/cmd-history.styl +42 -4
- package/client/components/footer/footer-entry.jsx +35 -5
- package/client/components/footer/footer.styl +8 -1
- package/client/components/footer/multi-tab-run-modal.jsx +83 -0
- package/client/components/footer/tab-select.jsx +34 -17
- package/client/components/layout/layout.jsx +23 -31
- package/client/components/layout/pixed.js +3 -0
- package/client/components/main/main.jsx +55 -28
- package/client/components/main/term-fullscreen.styl +9 -3
- package/client/components/quick-commands/qm.styl +3 -2
- package/client/components/quick-commands/quick-command-create-modal.jsx +60 -0
- package/client/components/quick-commands/quick-commands-form-elem.jsx +10 -4
- package/client/components/rdp/file-transfer.js +4 -4
- package/client/components/remote-monitor/monitor-details.jsx +458 -0
- package/client/components/remote-monitor/monitor-details.styl +87 -0
- package/client/components/remote-monitor/monitor-model.js +764 -0
- package/client/components/remote-monitor/remote-monitor-bar-entry.jsx +18 -0
- package/client/components/remote-monitor/remote-monitor-bar.jsx +344 -0
- package/client/components/remote-monitor/remote-monitor-bar.styl +106 -0
- package/client/components/remote-monitor/session-monitor.js +361 -0
- package/client/components/remote-monitor/use-monitor-details.js +10 -0
- package/client/components/remote-monitor/visibility.js +18 -0
- package/client/components/session/session-control.jsx +29 -2
- package/client/components/session/session.jsx +40 -18
- package/client/components/session/sessions.jsx +5 -2
- package/client/components/setting-panel/setting-common.jsx +25 -56
- package/client/components/setting-panel/setting-modal.jsx +57 -42
- package/client/components/setting-panel/setting-terminal.jsx +8 -2
- package/client/components/setting-panel/setting.styl +1 -1
- package/client/components/setting-panel/tab-triggers.jsx +71 -0
- package/client/components/sftp/file-compare-modal.jsx +6 -3
- package/client/components/sftp/file-item.jsx +5 -3
- package/client/components/sftp/owner-list.js +11 -2
- package/client/components/sftp/sftp-entry.jsx +4 -0
- package/client/components/side-panel-r/side-panel-r.jsx +2 -2
- package/client/components/sidebar/side-panel.jsx +1 -1
- package/client/components/sys-menu/menu-btn.jsx +30 -1
- package/client/components/tabs/index.jsx +2 -2
- package/client/components/tabs/tab-title.jsx +3 -2
- package/client/components/tabs/tab.jsx +5 -1
- package/client/components/terminal/attach-addon-custom.js +295 -49
- package/client/components/terminal/automation/index.js +64 -0
- package/client/components/terminal/automation/keys.js +49 -0
- package/client/components/terminal/automation/stream-matcher.js +82 -0
- package/client/components/terminal/automation/strip-ansi.js +19 -0
- package/client/components/terminal/automation/trigger-engine.js +233 -0
- package/client/components/terminal/automation/trigger-presets.js +110 -0
- package/client/components/terminal/command-tracker-addon.js +31 -1
- package/client/components/terminal/mixins/index.js +27 -0
- package/client/components/terminal/mixins/term-attach.js +123 -0
- package/client/components/terminal/mixins/term-config.jsx +84 -0
- package/client/components/terminal/mixins/term-content.js +126 -0
- package/client/components/terminal/mixins/term-context-menu.jsx +323 -0
- package/client/components/terminal/mixins/term-file-drop.js +149 -0
- package/client/components/terminal/mixins/term-init.js +122 -0
- package/client/components/terminal/mixins/term-log.jsx +96 -0
- package/client/components/terminal/mixins/term-resize.js +87 -0
- package/client/components/terminal/mixins/term-socket.js +433 -0
- package/client/components/terminal/mixins/term-suggestions.js +224 -0
- package/client/components/terminal/mixins/term-theme.js +190 -0
- package/client/components/terminal/mixins/term-touch.js +523 -0
- package/client/components/terminal/shell-detect.js +37 -0
- package/client/components/terminal/shell.js +0 -28
- package/client/components/terminal/shortcut-bar-defs.js +30 -4
- package/client/components/terminal/shortcut-bar-edit.jsx +40 -9
- package/client/components/terminal/shortcut-bar-entry.jsx +6 -3
- package/client/components/terminal/shortcut-bar.jsx +17 -10
- package/client/components/terminal/shortcut-bar.styl +4 -0
- package/client/components/terminal/ssh-reload-state.js +96 -0
- package/client/components/terminal/startup-queue.js +433 -0
- package/client/components/terminal/terminal-interactive.jsx +32 -0
- package/client/components/terminal/terminal-select-text.jsx +69 -0
- package/client/components/terminal/terminal.jsx +227 -2064
- package/client/components/terminal/terminal.styl +61 -0
- package/client/components/terminal/transfer-client-base.js +5 -29
- package/client/components/terminal/trzsz-client.js +1 -3
- package/client/components/terminal/xmodem-client.js +1 -3
- package/client/components/terminal/xterm-loader.js +7 -0
- package/client/components/terminal/zmodem-client.js +1 -3
- package/client/components/terminal-info/base.jsx +18 -70
- package/client/components/terminal-info/terminal-info-entry.jsx +13 -5
- package/client/components/terminal-info/terminal-info.jsx +50 -59
- package/client/components/terminal-info/terminal-info.styl +6 -49
- package/client/components/text-editor/open-text-editor.js +30 -0
- package/client/components/text-editor/text-editor-entry.jsx +17 -6
- package/client/components/text-editor/text-editor.jsx +14 -0
- package/client/components/triggers/trigger-editor.jsx +246 -0
- package/client/components/triggers/trigger-form.jsx +200 -0
- package/client/components/triggers/trigger-session-modal.jsx +199 -0
- package/client/components/triggers/trigger-setting-list.jsx +89 -0
- package/client/components/triggers/trigger-transport.jsx +60 -0
- package/client/components/triggers/triggers-field.jsx +19 -0
- package/client/components/widgets/widgets-list.jsx +11 -1
- package/client/css/mobile.styl +29 -8
- package/client/store/common.js +12 -1
- package/client/store/init-state.js +7 -0
- package/client/store/mcp-handler.js +158 -30
- package/client/store/setting.js +9 -0
- package/client/store/store.js +2 -0
- package/client/store/sync.js +2 -0
- package/client/store/tab.js +42 -3
- package/client/store/trigger.js +118 -0
- package/client/store/watch.js +13 -5
- package/client/views/index.pug +3 -0
- package/package.json +1 -1
- package/client/components/terminal-info/activity.jsx +0 -57
- package/client/components/terminal-info/data-cols-parser.jsx +0 -54
- package/client/components/terminal-info/disk.jsx +0 -45
- package/client/components/terminal-info/format-disks.js +0 -45
- package/client/components/terminal-info/network.jsx +0 -121
- package/client/components/terminal-info/resource.jsx +0 -99
- package/client/components/terminal-info/run-cmd.jsx +0 -263
- package/client/components/terminal-info/sys-info.jsx +0 -56
- package/client/components/terminal-info/up.jsx +0 -17
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
export const defaultAIPresets = [
|
|
2
|
+
{
|
|
3
|
+
id: 'atlascloud',
|
|
4
|
+
nameAI: 'AtlasCloud',
|
|
5
|
+
baseURLAI: 'https://api.atlascloud.ai/v1',
|
|
6
|
+
apiPathAI: '/chat/completions',
|
|
7
|
+
modelAI: 'deepseek-ai/deepseek-v4-flash',
|
|
8
|
+
authHeaderNameAI: 'Authorization: Bearer',
|
|
9
|
+
siteUrl: 'https://www.atlascloud.ai?ref=PCAEL2',
|
|
10
|
+
modelAIs: [
|
|
11
|
+
{ value: 'deepseek-ai/deepseek-v4-flash' },
|
|
12
|
+
{ value: 'zai-org/GLM-5.3' },
|
|
13
|
+
{ value: 'moonshotai/Kimi-K3' }
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'siliconflow',
|
|
18
|
+
nameAI: 'SiliconFlow',
|
|
19
|
+
baseURLAI: 'https://api.siliconflow.cn/v1',
|
|
20
|
+
apiPathAI: '/chat/completions',
|
|
21
|
+
modelAI: 'deepseek-ai/DeepSeek-V4-Flash',
|
|
22
|
+
authHeaderNameAI: 'Authorization: Bearer',
|
|
23
|
+
siteUrl: 'https://cloud.siliconflow.cn/i/Xa85Yz6R'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'ApiSmart',
|
|
27
|
+
nameAI: 'ApiSmart',
|
|
28
|
+
baseURLAI: 'https://api.apismart.ai/v1',
|
|
29
|
+
apiPathAI: '/chat/completions',
|
|
30
|
+
modelAI: 'DEEPSEEK_V4_FLASH',
|
|
31
|
+
authHeaderNameAI: 'Authorization: Bearer',
|
|
32
|
+
siteUrl: 'https://apismart.ai/?utm_source=electerm_app&utm_medium=link&utm_campaign=electerm'
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: 'apimart',
|
|
36
|
+
nameAI: 'ApiMart',
|
|
37
|
+
baseURLAI: 'https://api.apimart.ai/v1',
|
|
38
|
+
apiPathAI: '/chat/completions',
|
|
39
|
+
modelAI: 'deepseek-v4-flash',
|
|
40
|
+
authHeaderNameAI: 'Authorization: Bearer',
|
|
41
|
+
siteUrl: 'https://go.apimart.ai/gh-electerm'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
id: 'deepseek',
|
|
45
|
+
nameAI: 'DeepSeek',
|
|
46
|
+
baseURLAI: 'https://api.deepseek.com/v1',
|
|
47
|
+
apiPathAI: '/chat/completions',
|
|
48
|
+
modelAI: 'deepseek-chat',
|
|
49
|
+
authHeaderNameAI: 'Authorization: Bearer',
|
|
50
|
+
siteUrl: 'https://platform.deepseek.com/api_keys?utm_source=electerm_app&utm_medium=link&utm_campaign=electerm'
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'openai',
|
|
54
|
+
nameAI: 'OpenAI',
|
|
55
|
+
baseURLAI: 'https://api.openai.com/v1',
|
|
56
|
+
apiPathAI: '/chat/completions',
|
|
57
|
+
modelAI: 'gpt-4o',
|
|
58
|
+
authHeaderNameAI: 'Authorization: Bearer',
|
|
59
|
+
siteUrl: 'https://platform.openai.com/api-keys?utm_source=electerm_app&utm_medium=link&utm_campaign=electerm'
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'openai-responses',
|
|
63
|
+
nameAI: 'OpenAI (Responses API)',
|
|
64
|
+
baseURLAI: 'https://api.openai.com/v1',
|
|
65
|
+
apiPathAI: '/responses',
|
|
66
|
+
modelAI: 'gpt-4o',
|
|
67
|
+
authHeaderNameAI: 'Authorization: Bearer',
|
|
68
|
+
modelAIs: [
|
|
69
|
+
{ value: 'gpt-4o' },
|
|
70
|
+
{ value: 'gpt-4o-mini' },
|
|
71
|
+
{ value: 'gpt-4.1' },
|
|
72
|
+
{ value: 'o4-mini' }
|
|
73
|
+
],
|
|
74
|
+
siteUrl: 'https://platform.openai.com/api-keys?utm_source=electerm_app&utm_medium=link&utm_campaign=electerm'
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'anthropic',
|
|
78
|
+
nameAI: 'Anthropic Claude',
|
|
79
|
+
baseURLAI: 'https://api.anthropic.com/v1',
|
|
80
|
+
apiPathAI: '/messages',
|
|
81
|
+
modelAI: 'claude-sonnet-4-5',
|
|
82
|
+
authHeaderNameAI: 'x-api-key',
|
|
83
|
+
modelAIs: [
|
|
84
|
+
{ value: 'claude-sonnet-4-5' },
|
|
85
|
+
{ value: 'claude-opus-4-1' },
|
|
86
|
+
{ value: 'claude-haiku-4-5' }
|
|
87
|
+
],
|
|
88
|
+
siteUrl: 'https://console.anthropic.com/settings/keys?utm_source=electerm_app&utm_medium=link&utm_campaign=electerm'
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: 'openrouter',
|
|
92
|
+
nameAI: 'OpenRouter',
|
|
93
|
+
baseURLAI: 'https://openrouter.ai/api/v1',
|
|
94
|
+
apiPathAI: '/chat/completions',
|
|
95
|
+
modelAI: 'openai/gpt-4o-mini',
|
|
96
|
+
authHeaderNameAI: 'Authorization: Bearer',
|
|
97
|
+
siteUrl: 'https://openrouter.ai/keys?utm_source=electerm_app&utm_medium=link&utm_campaign=electerm'
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
id: 'orcarouter',
|
|
101
|
+
nameAI: 'OrcaRouter',
|
|
102
|
+
baseURLAI: 'https://api.orcarouter.ai/v1',
|
|
103
|
+
apiPathAI: '/chat/completions',
|
|
104
|
+
modelAI: 'orcarouter/free',
|
|
105
|
+
authHeaderNameAI: 'Authorization: Bearer',
|
|
106
|
+
siteUrl: 'https://www.orcarouter.ai/ref/ref_2c4884ca28b88a82593f'
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: 'google',
|
|
110
|
+
nameAI: 'Google Gemini',
|
|
111
|
+
baseURLAI: 'https://generativelanguage.googleapis.com/v1beta/openai',
|
|
112
|
+
apiPathAI: '/chat/completions',
|
|
113
|
+
modelAI: 'gemini-2.0-flash',
|
|
114
|
+
authHeaderNameAI: 'Authorization: Bearer',
|
|
115
|
+
siteUrl: 'https://aistudio.google.com/apikey'
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: 'groq',
|
|
119
|
+
nameAI: 'Groq',
|
|
120
|
+
baseURLAI: 'https://api.groq.com/openai/v1',
|
|
121
|
+
apiPathAI: '/chat/completions',
|
|
122
|
+
modelAI: 'llama-3.3-70b-versatile',
|
|
123
|
+
authHeaderNameAI: 'Authorization: Bearer'
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 'together',
|
|
127
|
+
nameAI: 'Together AI',
|
|
128
|
+
baseURLAI: 'https://api.together.xyz/v1',
|
|
129
|
+
apiPathAI: '/chat/completions',
|
|
130
|
+
modelAI: 'meta-llama/Llama-3.3-70B-Instruct-Turbo',
|
|
131
|
+
authHeaderNameAI: 'Authorization: Bearer'
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: 'mistral',
|
|
135
|
+
nameAI: 'Mistral AI',
|
|
136
|
+
baseURLAI: 'https://api.mistral.ai/v1',
|
|
137
|
+
apiPathAI: '/chat/completions',
|
|
138
|
+
modelAI: 'mistral-large-latest',
|
|
139
|
+
authHeaderNameAI: 'Authorization: Bearer'
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: 'xai',
|
|
143
|
+
nameAI: 'xAI (Grok)',
|
|
144
|
+
baseURLAI: 'https://api.x.ai/v1',
|
|
145
|
+
apiPathAI: '/chat/completions',
|
|
146
|
+
modelAI: 'grok-3-mini',
|
|
147
|
+
authHeaderNameAI: 'Authorization: Bearer'
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: 'perplexity',
|
|
151
|
+
nameAI: 'Perplexity',
|
|
152
|
+
baseURLAI: 'https://api.perplexity.ai',
|
|
153
|
+
apiPathAI: '/chat/completions',
|
|
154
|
+
modelAI: 'sonar',
|
|
155
|
+
authHeaderNameAI: 'Authorization: Bearer'
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
id: 'moonshot',
|
|
159
|
+
nameAI: 'Moonshot (Kimi)',
|
|
160
|
+
baseURLAI: 'https://api.moonshot.cn/v1',
|
|
161
|
+
apiPathAI: '/chat/completions',
|
|
162
|
+
modelAI: 'moonshot-v1-8k',
|
|
163
|
+
authHeaderNameAI: 'Authorization: Bearer'
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
export function getAIPresets () {
|
|
168
|
+
const presets = [...defaultAIPresets]
|
|
169
|
+
if (window.et?.defaultAIPreset) {
|
|
170
|
+
presets.unshift(window.et.defaultAIPreset)
|
|
171
|
+
}
|
|
172
|
+
return presets
|
|
173
|
+
}
|
|
@@ -79,6 +79,24 @@
|
|
|
79
79
|
opacity 0.4
|
|
80
80
|
cursor not-allowed
|
|
81
81
|
|
|
82
|
+
.ai-chat-attachments
|
|
83
|
+
padding 0 2px
|
|
84
|
+
.ant-tag
|
|
85
|
+
margin 0
|
|
86
|
+
max-width 100%
|
|
87
|
+
overflow hidden
|
|
88
|
+
text-overflow ellipsis
|
|
89
|
+
white-space nowrap
|
|
90
|
+
|
|
91
|
+
.ai-chat-item-attachments
|
|
92
|
+
.ant-tag
|
|
93
|
+
margin-right 4px
|
|
94
|
+
margin-bottom 4px
|
|
95
|
+
|
|
96
|
+
.ai-attachment-dragover
|
|
97
|
+
outline 2px dashed #1890ff
|
|
98
|
+
outline-offset -2px
|
|
99
|
+
|
|
82
100
|
.ai-chat-sessions-wrap
|
|
83
101
|
width 100%
|
|
84
102
|
height 100%
|
|
@@ -230,3 +248,68 @@
|
|
|
230
248
|
transform rotate(0deg)
|
|
231
249
|
to
|
|
232
250
|
transform rotate(360deg)
|
|
251
|
+
|
|
252
|
+
.ai-chat-empty
|
|
253
|
+
position relative
|
|
254
|
+
width 100%
|
|
255
|
+
min-height 150px
|
|
256
|
+
display flex
|
|
257
|
+
align-items center
|
|
258
|
+
justify-content center
|
|
259
|
+
overflow hidden
|
|
260
|
+
padding 10px 0
|
|
261
|
+
|
|
262
|
+
.ai-chat-empty-inner
|
|
263
|
+
text-align center
|
|
264
|
+
padding 0 12px
|
|
265
|
+
max-width 100%
|
|
266
|
+
|
|
267
|
+
.ai-chat-watermark
|
|
268
|
+
width 86%
|
|
269
|
+
max-width 400px
|
|
270
|
+
aspect-ratio 766 / 266
|
|
271
|
+
margin 0 auto 14px
|
|
272
|
+
background-color var(--text)
|
|
273
|
+
opacity 0.55
|
|
274
|
+
pointer-events none
|
|
275
|
+
user-select none
|
|
276
|
+
-webkit-mask-image var(--ai-watermark)
|
|
277
|
+
mask-image var(--ai-watermark)
|
|
278
|
+
-webkit-mask-repeat no-repeat
|
|
279
|
+
mask-repeat no-repeat
|
|
280
|
+
-webkit-mask-position center
|
|
281
|
+
mask-position center
|
|
282
|
+
-webkit-mask-size contain
|
|
283
|
+
mask-size contain
|
|
284
|
+
|
|
285
|
+
.ai-chat-empty-title
|
|
286
|
+
font-size 15px
|
|
287
|
+
font-weight 500
|
|
288
|
+
|
|
289
|
+
.ai-chat-empty-hint
|
|
290
|
+
font-size 12px
|
|
291
|
+
margin-top 4px
|
|
292
|
+
color var(--text-dark)
|
|
293
|
+
|
|
294
|
+
.ai-chat-empty-examples
|
|
295
|
+
margin-top 14px
|
|
296
|
+
display flex
|
|
297
|
+
flex-direction column
|
|
298
|
+
align-items center
|
|
299
|
+
gap 6px
|
|
300
|
+
|
|
301
|
+
.ai-chat-empty-example
|
|
302
|
+
max-width 100%
|
|
303
|
+
font-size 12px
|
|
304
|
+
padding 5px 10px
|
|
305
|
+
border 1px solid var(--main-darker)
|
|
306
|
+
border-radius 14px
|
|
307
|
+
color var(--text-dark)
|
|
308
|
+
cursor pointer
|
|
309
|
+
transition all 0.2s
|
|
310
|
+
white-space nowrap
|
|
311
|
+
overflow hidden
|
|
312
|
+
text-overflow ellipsis
|
|
313
|
+
&:hover
|
|
314
|
+
border-color var(--primary)
|
|
315
|
+
color var(--text)
|
|
@@ -7,6 +7,10 @@ import { generateMosaicBackground } from './shapes'
|
|
|
7
7
|
|
|
8
8
|
const themeDomId = 'css-overwrite-terminal-backgrounds'
|
|
9
9
|
|
|
10
|
+
// natural (intrinsic) width of images/electerm-watermark.png, used to keep the
|
|
11
|
+
// default terminal background at its original size on wide terminals
|
|
12
|
+
const watermarkWidth = 766
|
|
13
|
+
|
|
10
14
|
function createBackgroundStyle (imagePath) {
|
|
11
15
|
if (!imagePath || imagePath === '') {
|
|
12
16
|
return ''
|
|
@@ -104,8 +108,15 @@ async function createStyleForTab (tab, props) {
|
|
|
104
108
|
async function createGlobalStyle (props) {
|
|
105
109
|
const st = await createBackgroundStyle(props.terminalBackgroundImagePath)
|
|
106
110
|
if (!st) {
|
|
111
|
+
// Default terminal bg = the electerm watermark (766 x 266 natural size).
|
|
112
|
+
// Without an explicit size the image is just centered at 1:1, so on narrow
|
|
113
|
+
// panes (mobile / thin split columns) it gets cropped on both sides.
|
|
114
|
+
// `min(100%, 766px) auto` keeps the aspect ratio and scales it down to fit
|
|
115
|
+
// the pane width, while leaving the desktop rendering (= natural size)
|
|
116
|
+
// untouched.
|
|
107
117
|
return '#container .session-batch-active .xterm-screen::before {' +
|
|
108
118
|
'background-image: url("./images/electerm-watermark.png");' +
|
|
119
|
+
`background-size: min(100%, ${watermarkWidth}px) auto;` +
|
|
109
120
|
'}'
|
|
110
121
|
}
|
|
111
122
|
|
|
@@ -25,6 +25,7 @@ const bookmarkSchema = {
|
|
|
25
25
|
compress: 'array - compression algorithms (zlib@openssh.com, zlib, none)',
|
|
26
26
|
runScripts: 'array - run scripts after connected ({delay,script})',
|
|
27
27
|
quickCommands: 'array - quick commands ({name,command})',
|
|
28
|
+
triggers: 'array - declarative auto triggers ({id,name,enabled,match:{type:text|regex,value,caseSensitive},action:{type:send|notify,value},sendEnter,mode:repeat|once|cooldown,cooldownMs})',
|
|
28
29
|
proxy: 'string - proxy address (socks5://...)',
|
|
29
30
|
proxyCommand: 'string - ssh proxy command, supports %h (host) %p (port) %r (username) placeholders, e.g. `netbird ssh proxy %h %p` or `cloudflared access ssh --hostname %h`',
|
|
30
31
|
x11: 'boolean - enable x11 forwarding, default is false',
|
|
@@ -66,6 +67,7 @@ const bookmarkSchema = {
|
|
|
66
67
|
loginPrompt: 'string - login prompt regex',
|
|
67
68
|
passwordPrompt: 'string - password prompt regex',
|
|
68
69
|
runScripts: 'array - run scripts after connected ({delay,script})',
|
|
70
|
+
triggers: 'array - declarative auto triggers, same shape as ssh.triggers',
|
|
69
71
|
startDirectoryRemote: 'string - remote starting directory',
|
|
70
72
|
startDirectoryLocal: 'string - local starting directory',
|
|
71
73
|
profile: 'string - profile id',
|
|
@@ -88,6 +90,7 @@ const bookmarkSchema = {
|
|
|
88
90
|
closeSequence: 'string - key sequence sent to the serial port when the user clicks "exit gracefully" in the terminal controls, supports \\n \\t \\r \\\\ and \\xHH hex bytes, default "\\x01ky" (Ctrl+A, k, y - GNU screen kill-window confirm)',
|
|
89
91
|
closeSequenceDelay: 'number - ms to wait after sending closeSequence before closing the port, default 500',
|
|
90
92
|
runScripts: 'array - run scripts after connected ({delay,script})',
|
|
93
|
+
triggers: 'array - declarative auto triggers, same shape as ssh.triggers',
|
|
91
94
|
description: 'string - bookmark description'
|
|
92
95
|
},
|
|
93
96
|
vnc: {
|
|
@@ -144,6 +147,7 @@ const bookmarkSchema = {
|
|
|
144
147
|
description: 'string - bookmark description',
|
|
145
148
|
startDirectoryLocal: 'string - local starting directory',
|
|
146
149
|
runScripts: 'array - run scripts after connected ({delay,script})',
|
|
150
|
+
triggers: 'array - declarative auto triggers, same shape as ssh.triggers',
|
|
147
151
|
execWindows: 'string - Windows exec path (overrides global setting)',
|
|
148
152
|
execMac: 'string - Mac exec path (overrides global setting)',
|
|
149
153
|
execLinux: 'string - Linux exec path (overrides global setting)',
|
|
@@ -16,6 +16,7 @@ import ConnectionHopping from './connection-hopping.jsx'
|
|
|
16
16
|
import TerminalBackgroundField from './terminal-background.jsx'
|
|
17
17
|
import ExecSettingsField from './exec-settings-field.jsx'
|
|
18
18
|
import useQuickCmds from './quick-commands.jsx'
|
|
19
|
+
import TriggersField from '../../triggers/triggers-field.jsx'
|
|
19
20
|
import ProfileItem from './profile-item.jsx'
|
|
20
21
|
import renderRunScripts from './run-scripts.jsx'
|
|
21
22
|
import SerialPathSelector from './serial-path-selector.jsx'
|
|
@@ -173,6 +174,8 @@ export function renderFormItem (item, formItemLayout, form, ctxProps, index) {
|
|
|
173
174
|
return <ProfileItem key={name} store={ctxProps.store} form={form} profileFilter={item.profileFilter} />
|
|
174
175
|
case 'quickCommands':
|
|
175
176
|
return <Fragment key={name}>{useQuickCmds(form, ctxProps.formData || {})}</Fragment>
|
|
177
|
+
case 'triggers':
|
|
178
|
+
return <TriggersField key={name} />
|
|
176
179
|
case 'runScripts':
|
|
177
180
|
return <Fragment key={name}>{renderRunScripts()}</Fragment>
|
|
178
181
|
case 'serialPathSelector':
|
|
@@ -251,6 +251,12 @@ export const commonFields = {
|
|
|
251
251
|
label: ''
|
|
252
252
|
},
|
|
253
253
|
|
|
254
|
+
triggers: {
|
|
255
|
+
type: 'triggers',
|
|
256
|
+
name: 'triggers',
|
|
257
|
+
label: ''
|
|
258
|
+
},
|
|
259
|
+
|
|
254
260
|
enableTerminalImage: {
|
|
255
261
|
type: 'switch',
|
|
256
262
|
name: 'enableTerminalImage',
|
|
@@ -360,6 +366,12 @@ export const quickCommandsTab = () => ({
|
|
|
360
366
|
fields: [commonFields.quickCommands]
|
|
361
367
|
})
|
|
362
368
|
|
|
369
|
+
export const triggersTab = () => ({
|
|
370
|
+
key: 'triggers',
|
|
371
|
+
label: e('triggers'),
|
|
372
|
+
fields: [commonFields.triggers]
|
|
373
|
+
})
|
|
374
|
+
|
|
363
375
|
export const sshTunnelTab = () => ({
|
|
364
376
|
key: 'tunnel',
|
|
365
377
|
label: e('sshTunnel'),
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
getTerminalBackgroundDefaults
|
|
8
8
|
} from '../common/init-values.js'
|
|
9
9
|
import defaultSettings from '../../../common/default-setting.js'
|
|
10
|
-
import { commonFields } from './common-fields.js'
|
|
10
|
+
import { commonFields, triggersTab } from './common-fields.js'
|
|
11
11
|
|
|
12
12
|
const e = window.translate
|
|
13
13
|
|
|
@@ -56,7 +56,8 @@ const localConfig = {
|
|
|
56
56
|
fields: [
|
|
57
57
|
commonFields.quickCommands
|
|
58
58
|
]
|
|
59
|
-
}
|
|
59
|
+
},
|
|
60
|
+
triggersTab()
|
|
60
61
|
]
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -2,7 +2,7 @@ import { formItemLayout } from '../../../common/form-layout.js'
|
|
|
2
2
|
import { terminalSerialType, commonBaudRates, commonDataBits, commonStopBits, commonParities, commonTxLineEndings, commonRxLineEndings } from '../../../common/constants.js'
|
|
3
3
|
import defaultSettings from '../../../common/default-setting.js'
|
|
4
4
|
import { createBaseInitValues, getTerminalBackgroundDefaults } from '../common/init-values.js'
|
|
5
|
-
import { commonFields } from './common-fields.js'
|
|
5
|
+
import { commonFields, triggersTab } from './common-fields.js'
|
|
6
6
|
|
|
7
7
|
const e = window.translate
|
|
8
8
|
|
|
@@ -91,7 +91,8 @@ const serialConfig = {
|
|
|
91
91
|
fields: [
|
|
92
92
|
{ type: 'quickCommands', name: '__quick__', label: '' }
|
|
93
93
|
]
|
|
94
|
-
}
|
|
94
|
+
},
|
|
95
|
+
triggersTab()
|
|
95
96
|
]
|
|
96
97
|
}
|
|
97
98
|
|
|
@@ -3,7 +3,7 @@ import { formItemLayout } from '../../../common/form-layout.js'
|
|
|
3
3
|
import { connectionMap, authTypeMap, defaultEnvLang } from '../../../common/constants.js'
|
|
4
4
|
import defaultSetting from '../../../common/default-setting.js'
|
|
5
5
|
import { createBaseInitValues, getTerminalDefaults, getSshDefaults, getTerminalBackgroundDefaults, getAuthTypeDefault } from '../common/init-values.js'
|
|
6
|
-
import { sshAuthFields, sshSettings, quickCommandsTab, sshTunnelTab, connectionHoppingTab } from './common-fields.js'
|
|
6
|
+
import { sshAuthFields, sshSettings, quickCommandsTab, triggersTab, sshTunnelTab, connectionHoppingTab } from './common-fields.js'
|
|
7
7
|
|
|
8
8
|
const e = window.translate
|
|
9
9
|
|
|
@@ -44,6 +44,7 @@ const sshConfig = {
|
|
|
44
44
|
fields: sshSettings
|
|
45
45
|
},
|
|
46
46
|
quickCommandsTab(),
|
|
47
|
+
triggersTab(),
|
|
47
48
|
sshTunnelTab(),
|
|
48
49
|
connectionHoppingTab()
|
|
49
50
|
]
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
getTerminalBackgroundDefaults,
|
|
8
8
|
getAuthTypeDefault
|
|
9
9
|
} from '../common/init-values.js'
|
|
10
|
-
import { telnetAuthFields, terminalSettings, quickCommandsTab } from './common-fields.js'
|
|
10
|
+
import { telnetAuthFields, terminalSettings, quickCommandsTab, triggersTab } from './common-fields.js'
|
|
11
11
|
|
|
12
12
|
const e = window.translate
|
|
13
13
|
|
|
@@ -39,7 +39,8 @@ const telnetConfig = {
|
|
|
39
39
|
label: e('settings'),
|
|
40
40
|
fields: terminalSettings
|
|
41
41
|
},
|
|
42
|
-
quickCommandsTab()
|
|
42
|
+
quickCommandsTab(),
|
|
43
|
+
triggersTab()
|
|
43
44
|
]
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reusable filter control: a filter icon button that opens a checkbox list.
|
|
3
|
+
* Shared by the terminal info panel and the remote monitor bar.
|
|
4
|
+
*/
|
|
5
|
+
import { Popover } from 'antd'
|
|
6
|
+
import { CheckOutlined, FilterOutlined } from '@ant-design/icons'
|
|
7
|
+
import './item-filter.styl'
|
|
8
|
+
|
|
9
|
+
const e = window.translate
|
|
10
|
+
|
|
11
|
+
export default function ItemFilter (props) {
|
|
12
|
+
const {
|
|
13
|
+
ids = [],
|
|
14
|
+
selected,
|
|
15
|
+
onToggle,
|
|
16
|
+
onOpenChange,
|
|
17
|
+
placement = 'bottomRight',
|
|
18
|
+
className = ''
|
|
19
|
+
} = props
|
|
20
|
+
const chosen = selected instanceof Set
|
|
21
|
+
? selected
|
|
22
|
+
: new Set(selected || [])
|
|
23
|
+
const title = e('filter')
|
|
24
|
+
const total = ids.length
|
|
25
|
+
const count = ids.filter(id => chosen.has(id)).length
|
|
26
|
+
const content = (
|
|
27
|
+
<div className='item-filter-list' role='menu'>
|
|
28
|
+
{
|
|
29
|
+
ids.map(id => {
|
|
30
|
+
const active = chosen.has(id)
|
|
31
|
+
return (
|
|
32
|
+
<div
|
|
33
|
+
aria-checked={active}
|
|
34
|
+
className={'item-filter-item' + (active ? ' item-filter-item-on' : '')}
|
|
35
|
+
data-filter-item={id}
|
|
36
|
+
key={id}
|
|
37
|
+
onClick={() => onToggle(id)}
|
|
38
|
+
onKeyDown={event => {
|
|
39
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
40
|
+
event.preventDefault()
|
|
41
|
+
onToggle(id)
|
|
42
|
+
}
|
|
43
|
+
}}
|
|
44
|
+
role='menuitemcheckbox'
|
|
45
|
+
tabIndex={0}
|
|
46
|
+
>
|
|
47
|
+
<span className='item-filter-check'>
|
|
48
|
+
{active ? <CheckOutlined /> : null}
|
|
49
|
+
</span>
|
|
50
|
+
<span className='item-filter-label'>{e(id)}</span>
|
|
51
|
+
</div>
|
|
52
|
+
)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
57
|
+
return (
|
|
58
|
+
<Popover
|
|
59
|
+
content={content}
|
|
60
|
+
onOpenChange={onOpenChange}
|
|
61
|
+
placement={placement}
|
|
62
|
+
title={title}
|
|
63
|
+
trigger='click'
|
|
64
|
+
>
|
|
65
|
+
<button
|
|
66
|
+
aria-label={`${title} (${count}/${total})`}
|
|
67
|
+
className={'item-filter' + (className ? ` ${className}` : '')}
|
|
68
|
+
title={title}
|
|
69
|
+
type='button'
|
|
70
|
+
>
|
|
71
|
+
<FilterOutlined />
|
|
72
|
+
<span className='item-filter-count'>({count}/{total})</span>
|
|
73
|
+
</button>
|
|
74
|
+
</Popover>
|
|
75
|
+
)
|
|
76
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
.item-filter
|
|
2
|
+
display inline-flex
|
|
3
|
+
align-items center
|
|
4
|
+
justify-content center
|
|
5
|
+
gap 3px
|
|
6
|
+
height 24px
|
|
7
|
+
padding 0 4px
|
|
8
|
+
border 0
|
|
9
|
+
background transparent
|
|
10
|
+
color var(--text-dark)
|
|
11
|
+
font-size 14px
|
|
12
|
+
cursor pointer
|
|
13
|
+
&:hover,
|
|
14
|
+
&:focus-visible
|
|
15
|
+
color var(--text)
|
|
16
|
+
&:focus-visible
|
|
17
|
+
outline 1px solid var(--primary)
|
|
18
|
+
outline-offset 1px
|
|
19
|
+
|
|
20
|
+
.item-filter-count
|
|
21
|
+
color var(--text-disabled)
|
|
22
|
+
font-size 11px
|
|
23
|
+
font-variant-numeric tabular-nums
|
|
24
|
+
|
|
25
|
+
.item-filter-list
|
|
26
|
+
min-width 140px
|
|
27
|
+
padding 2px 0
|
|
28
|
+
|
|
29
|
+
.item-filter-item
|
|
30
|
+
display flex
|
|
31
|
+
align-items center
|
|
32
|
+
gap 6px
|
|
33
|
+
padding 4px 8px
|
|
34
|
+
color var(--text)
|
|
35
|
+
cursor pointer
|
|
36
|
+
&:hover
|
|
37
|
+
background var(--main-light)
|
|
38
|
+
&:focus-visible
|
|
39
|
+
outline 1px solid var(--primary)
|
|
40
|
+
outline-offset -1px
|
|
41
|
+
|
|
42
|
+
.item-filter-item-on
|
|
43
|
+
color var(--primary)
|
|
44
|
+
|
|
45
|
+
.item-filter-check
|
|
46
|
+
flex-shrink 0
|
|
47
|
+
width 12px
|
|
48
|
+
font-size 10px
|
|
49
|
+
|
|
50
|
+
.item-filter-label
|
|
51
|
+
min-width 0
|
|
52
|
+
overflow hidden
|
|
53
|
+
text-overflow ellipsis
|
|
54
|
+
white-space nowrap
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Component } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error boundary for locally lazy-loaded components.
|
|
5
|
+
*
|
|
6
|
+
* A failed chunk fetch (stale chunk after a web-app redeploy, flaky network)
|
|
7
|
+
* throws during render. Without a boundary like this one the error bubbles to
|
|
8
|
+
* the root ErrorBoundary in `main/index.jsx`, which swaps the *whole* app —
|
|
9
|
+
* every open terminal/ssh tab included — for the error page. Far too much
|
|
10
|
+
* collateral for one small chunk that failed to load.
|
|
11
|
+
*
|
|
12
|
+
* Renders nothing when the child failed to load, reports the error through the
|
|
13
|
+
* app's notification, and lets the caller react (e.g. release a mount latch so
|
|
14
|
+
* the next request retries).
|
|
15
|
+
*
|
|
16
|
+
* Note: `React.lazy` caches a rejected `import()` forever, so retrying needs a
|
|
17
|
+
* *new* lazy component — see `text-editor-entry.jsx` for the `useMemo` version.
|
|
18
|
+
*/
|
|
19
|
+
export default class LazyBoundary extends Component {
|
|
20
|
+
state = { failed: false }
|
|
21
|
+
|
|
22
|
+
static getDerivedStateFromError () {
|
|
23
|
+
return { failed: true }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
componentDidCatch (error) {
|
|
27
|
+
window.store.onError(error)
|
|
28
|
+
this.props.onError?.(error)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
render () {
|
|
32
|
+
return this.state.failed ? null : this.props.children
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -11,7 +11,9 @@ const opacityDomId = 'opacity-style'
|
|
|
11
11
|
* @returns {null}
|
|
12
12
|
*/
|
|
13
13
|
export default function Opacity ({ opacity }) {
|
|
14
|
-
// Default to 1 if opacity is not provided
|
|
14
|
+
// Default to 1 if opacity is not provided.
|
|
15
|
+
// window opacity is electron-only: this component is lazy-loaded and never
|
|
16
|
+
// rendered (nor loaded) in web app — see main.jsx
|
|
15
17
|
const currentOpacity = opacity !== undefined ? opacity : 1
|
|
16
18
|
const prevRef = useRef(null)
|
|
17
19
|
|