@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
|
@@ -3,13 +3,24 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { useState, useEffect } from 'react'
|
|
6
|
-
import { Button, Empty, Popover } from 'antd'
|
|
6
|
+
import { Button, Empty, Popover, Dropdown } from 'antd'
|
|
7
7
|
import { auto } from 'manate/react'
|
|
8
8
|
import SwitchLabel from '../common/switch'
|
|
9
9
|
import { copy } from '../../common/clipboard'
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
HistoryOutlined,
|
|
12
|
+
DeleteOutlined,
|
|
13
|
+
CopyOutlined,
|
|
14
|
+
UnorderedListOutlined,
|
|
15
|
+
MoreOutlined,
|
|
16
|
+
PlusOutlined,
|
|
17
|
+
CodeOutlined
|
|
18
|
+
} from '@ant-design/icons'
|
|
11
19
|
import InputAutoFocus from '../common/input-auto-focus'
|
|
12
20
|
import { getItemJSON, setItemJSON } from '../../common/safe-local-storage'
|
|
21
|
+
import QuickCommandCreateModal from '../quick-commands/quick-command-create-modal'
|
|
22
|
+
import MultiTabRunModal from './multi-tab-run-modal'
|
|
23
|
+
import classNames from 'classnames'
|
|
13
24
|
import './cmd-history.styl'
|
|
14
25
|
|
|
15
26
|
const e = window.translate
|
|
@@ -20,6 +31,10 @@ export default auto(function CmdHistory (props) {
|
|
|
20
31
|
const [sortByFrequency, setSortByFrequency] = useState(() => {
|
|
21
32
|
return getItemJSON(SORT_BY_FREQ_KEY, false)
|
|
22
33
|
})
|
|
34
|
+
// cmd of the item whose action menu is open / being acted on
|
|
35
|
+
const [menuOpenCmd, setMenuOpenCmd] = useState('')
|
|
36
|
+
const [quickCommandCmd, setQuickCommandCmd] = useState('')
|
|
37
|
+
const [multiTabCmd, setMultiTabCmd] = useState('')
|
|
23
38
|
const { terminalCommandHistory } = props.store
|
|
24
39
|
|
|
25
40
|
useEffect(() => {
|
|
@@ -30,11 +45,6 @@ export default auto(function CmdHistory (props) {
|
|
|
30
45
|
window.store.runCmdFromHistory(cmd)
|
|
31
46
|
}
|
|
32
47
|
|
|
33
|
-
function handleDeleteCommand (cmd, ev) {
|
|
34
|
-
ev.stopPropagation()
|
|
35
|
-
window.store.deleteCmdHistory(cmd)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
48
|
function handleCopyCommand (cmd, ev) {
|
|
39
49
|
ev.stopPropagation()
|
|
40
50
|
copy(cmd)
|
|
@@ -44,6 +54,46 @@ export default auto(function CmdHistory (props) {
|
|
|
44
54
|
window.store.clearAllCmdHistory()
|
|
45
55
|
}
|
|
46
56
|
|
|
57
|
+
function handleMenuAction (key, cmd) {
|
|
58
|
+
if (key === 'delete') {
|
|
59
|
+
window.store.deleteCmdHistory(cmd)
|
|
60
|
+
} else if (key === 'quickCommand') {
|
|
61
|
+
setQuickCommandCmd(cmd)
|
|
62
|
+
} else if (key === 'multiTab') {
|
|
63
|
+
window.store.filterBatchInputSelectedTabIds()
|
|
64
|
+
setMultiTabCmd(cmd)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function getMenuProps (cmd) {
|
|
69
|
+
return {
|
|
70
|
+
items: [
|
|
71
|
+
{
|
|
72
|
+
key: 'delete',
|
|
73
|
+
icon: <DeleteOutlined />,
|
|
74
|
+
label: e('del'),
|
|
75
|
+
danger: true
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
key: 'quickCommand',
|
|
79
|
+
icon: <PlusOutlined />,
|
|
80
|
+
label: e('addQuickCommands')
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
key: 'multiTab',
|
|
84
|
+
icon: <CodeOutlined />,
|
|
85
|
+
label: e('runInAllTerminals')
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
onClick: ({ key, domEvent }) => {
|
|
89
|
+
// the menu renders in a portal but React events still bubble through
|
|
90
|
+
// the row, which would run the command
|
|
91
|
+
domEvent.stopPropagation()
|
|
92
|
+
handleMenuAction(key, cmd)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
47
97
|
function filterArray (array, keyword) {
|
|
48
98
|
if (!keyword) {
|
|
49
99
|
return array
|
|
@@ -76,34 +126,46 @@ export default auto(function CmdHistory (props) {
|
|
|
76
126
|
/>
|
|
77
127
|
)
|
|
78
128
|
}
|
|
79
|
-
return filtered.map((item, index) =>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
<
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
129
|
+
return filtered.map((item, index) => {
|
|
130
|
+
const cls = classNames(
|
|
131
|
+
'cmd-history-item',
|
|
132
|
+
{ 'menu-open': menuOpenCmd === item.cmd }
|
|
133
|
+
)
|
|
134
|
+
return (
|
|
135
|
+
<div
|
|
136
|
+
key={index}
|
|
137
|
+
className={cls}
|
|
138
|
+
onClick={() => handleRunCommand(item.cmd)}
|
|
139
|
+
>
|
|
140
|
+
<span className='cmd-history-item-text' title={item.cmd}>{item.cmd}</span>
|
|
141
|
+
<div className='cmd-history-item-actions'>
|
|
142
|
+
<span className='cmd-history-item-count' title={e('count') + ': ' + item.count}>
|
|
143
|
+
{item.count}
|
|
144
|
+
</span>
|
|
145
|
+
<Button
|
|
146
|
+
type='text'
|
|
147
|
+
size='small'
|
|
148
|
+
icon={<CopyOutlined />}
|
|
149
|
+
className='cmd-history-item-copy'
|
|
150
|
+
onClick={(ev) => handleCopyCommand(item.cmd, ev)}
|
|
151
|
+
/>
|
|
152
|
+
<Dropdown
|
|
153
|
+
menu={getMenuProps(item.cmd)}
|
|
154
|
+
trigger={['click']}
|
|
155
|
+
onOpenChange={(open) => setMenuOpenCmd(open ? item.cmd : '')}
|
|
156
|
+
>
|
|
157
|
+
<Button
|
|
158
|
+
type='text'
|
|
159
|
+
size='small'
|
|
160
|
+
icon={<MoreOutlined />}
|
|
161
|
+
className='cmd-history-item-more'
|
|
162
|
+
onClick={(ev) => ev.stopPropagation()}
|
|
163
|
+
/>
|
|
164
|
+
</Dropdown>
|
|
165
|
+
</div>
|
|
104
166
|
</div>
|
|
105
|
-
|
|
106
|
-
)
|
|
167
|
+
)
|
|
168
|
+
})
|
|
107
169
|
}
|
|
108
170
|
|
|
109
171
|
function renderHeader () {
|
|
@@ -148,16 +210,41 @@ export default auto(function CmdHistory (props) {
|
|
|
148
210
|
)
|
|
149
211
|
|
|
150
212
|
return (
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
213
|
+
<>
|
|
214
|
+
<Popover
|
|
215
|
+
content={content}
|
|
216
|
+
trigger='click'
|
|
217
|
+
placement='topLeft'
|
|
218
|
+
>
|
|
219
|
+
<Button
|
|
220
|
+
size='small'
|
|
221
|
+
type='text'
|
|
222
|
+
icon={<HistoryOutlined />}
|
|
223
|
+
/>
|
|
224
|
+
</Popover>
|
|
225
|
+
{
|
|
226
|
+
quickCommandCmd
|
|
227
|
+
? (
|
|
228
|
+
<QuickCommandCreateModal
|
|
229
|
+
key={quickCommandCmd}
|
|
230
|
+
store={props.store}
|
|
231
|
+
command={quickCommandCmd}
|
|
232
|
+
onClose={() => setQuickCommandCmd('')}
|
|
233
|
+
/>
|
|
234
|
+
)
|
|
235
|
+
: null
|
|
236
|
+
}
|
|
237
|
+
{
|
|
238
|
+
multiTabCmd
|
|
239
|
+
? (
|
|
240
|
+
<MultiTabRunModal
|
|
241
|
+
store={props.store}
|
|
242
|
+
cmd={multiTabCmd}
|
|
243
|
+
onClose={() => setMultiTabCmd('')}
|
|
244
|
+
/>
|
|
245
|
+
)
|
|
246
|
+
: null
|
|
247
|
+
}
|
|
248
|
+
</>
|
|
162
249
|
)
|
|
163
250
|
})
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
overflow hidden
|
|
5
5
|
display flex
|
|
6
6
|
flex-direction column
|
|
7
|
-
|
|
8
7
|
.cmd-history-search
|
|
9
8
|
flex-shrink 0
|
|
10
9
|
position sticky
|
|
@@ -46,6 +45,13 @@
|
|
|
46
45
|
cursor pointer
|
|
47
46
|
&:hover
|
|
48
47
|
background var(--main-lighter)
|
|
48
|
+
// keep the row actions visible while its action menu is open, otherwise the
|
|
49
|
+
// menu trigger would be hidden (display none) and the popup would lose its
|
|
50
|
+
// anchor when the mouse moves onto the menu
|
|
51
|
+
&.menu-open
|
|
52
|
+
background var(--main-lighter)
|
|
53
|
+
.cmd-history-item-actions
|
|
54
|
+
display flex
|
|
49
55
|
|
|
50
56
|
.cmd-history-item-text
|
|
51
57
|
overflow hidden
|
|
@@ -68,14 +74,46 @@
|
|
|
68
74
|
.cmd-history-item:hover &
|
|
69
75
|
display flex
|
|
70
76
|
|
|
71
|
-
.cmd-history-item-
|
|
77
|
+
.cmd-history-item-more
|
|
72
78
|
cursor pointer
|
|
73
79
|
padding 2px 4px
|
|
74
|
-
&:hover
|
|
75
|
-
color red
|
|
76
80
|
|
|
77
81
|
.cmd-history-item-copy
|
|
78
82
|
cursor pointer
|
|
79
83
|
padding 2px 4px
|
|
80
84
|
&:hover
|
|
81
85
|
color blue
|
|
86
|
+
|
|
87
|
+
.multi-tab-run-cmd
|
|
88
|
+
font-size 12px
|
|
89
|
+
padding 6px 8px
|
|
90
|
+
margin-bottom 10px
|
|
91
|
+
border-radius 4px
|
|
92
|
+
background var(--main-lighter)
|
|
93
|
+
word-break break-all
|
|
94
|
+
max-height 96px
|
|
95
|
+
overflow-y auto
|
|
96
|
+
|
|
97
|
+
// Narrow screens: the popup is aligned to the history trigger (placement
|
|
98
|
+
// topLeft) and is 324px wide, while the footer trigger sits within the first
|
|
99
|
+
// ~96px of the screen (43px icon bar + footer margin + the icons before it), so
|
|
100
|
+
// below ~420px the popup's right edge would fall outside the window. antd's
|
|
101
|
+
// auto-shift does not correct it, so pin the popup to the viewport instead.
|
|
102
|
+
// Matched by its content: the popup lives in a body-level portal, so neither
|
|
103
|
+
// the component tree nor an antd popup class reaches it reliably.
|
|
104
|
+
@media (max-width: 420px)
|
|
105
|
+
.ant-popover:has(.cmd-history-popover-content)
|
|
106
|
+
// rc-trigger writes `inset: auto auto 40px <trigger left>px` inline
|
|
107
|
+
left 0px !important
|
|
108
|
+
// pinned to the viewport, the arrow no longer points at the trigger
|
|
109
|
+
.ant-popover-arrow
|
|
110
|
+
display none
|
|
111
|
+
.cmd-history-popover-content
|
|
112
|
+
// popup = content + 24px container padding; keep 8px on both sides
|
|
113
|
+
width 100%
|
|
114
|
+
|
|
115
|
+
// Touch devices have no hover, so the row actions (count / copy / ⋯ menu)
|
|
116
|
+
// would never be reachable — feature-detect instead of sniffing the UA
|
|
117
|
+
@media (pointer: coarse)
|
|
118
|
+
.cmd-history-item-actions
|
|
119
|
+
display flex
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import { auto } from 'manate/react'
|
|
2
2
|
import {
|
|
3
3
|
Select,
|
|
4
|
-
Dropdown
|
|
4
|
+
Dropdown,
|
|
5
|
+
Badge
|
|
5
6
|
} from 'antd'
|
|
6
|
-
import {
|
|
7
|
+
import { BarChartOutlined, TranslationOutlined, DoubleRightOutlined, FunctionOutlined } from '@ant-design/icons'
|
|
7
8
|
import './footer.styl'
|
|
8
9
|
import { statusMap } from '../../common/constants'
|
|
9
10
|
import BatchInput from './batch-input'
|
|
10
11
|
import encodes from '../bookmark-form/common/encodes'
|
|
11
12
|
import { refs } from '../common/ref'
|
|
12
13
|
import Qm from '../quick-commands/quick-commands-select'
|
|
14
|
+
import TriggerSessionModal from '../triggers/trigger-session-modal'
|
|
13
15
|
import AIIcon from '../icons/ai-icon'
|
|
14
16
|
import { isAIDisabled } from '../../common/ai-feature'
|
|
15
17
|
import CmdHistory from './cmd-history'
|
|
16
18
|
|
|
19
|
+
const e = window.translate
|
|
20
|
+
|
|
17
21
|
const {
|
|
18
22
|
Option
|
|
19
23
|
} = Select
|
|
20
24
|
|
|
21
|
-
const e = window.translate
|
|
22
|
-
|
|
23
25
|
export default auto(function FooterEntry (props) {
|
|
24
26
|
function handleInfoPanel () {
|
|
25
27
|
window.store.openInfoPanel()
|
|
@@ -77,6 +79,33 @@ export default auto(function FooterEntry (props) {
|
|
|
77
79
|
)
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
function renderTriggers () {
|
|
83
|
+
const { store } = props
|
|
84
|
+
const tab = store.currentTab
|
|
85
|
+
let count = 0
|
|
86
|
+
try {
|
|
87
|
+
count = tab ? store.getEffectiveTriggers(tab).length : store.triggers.length
|
|
88
|
+
} catch (err) {
|
|
89
|
+
count = 0
|
|
90
|
+
}
|
|
91
|
+
return (
|
|
92
|
+
<div className='terminal-footer-unit terminal-footer-triggers'>
|
|
93
|
+
<Badge
|
|
94
|
+
count={count}
|
|
95
|
+
size='small'
|
|
96
|
+
offset={[-2, 2]}
|
|
97
|
+
>
|
|
98
|
+
<FunctionOutlined
|
|
99
|
+
onClick={() => store.toggleTriggerSessionModal(true)}
|
|
100
|
+
className='pointer font14 terminal-trigger-icon'
|
|
101
|
+
title={e('triggers')}
|
|
102
|
+
/>
|
|
103
|
+
</Badge>
|
|
104
|
+
<TriggerSessionModal store={store} />
|
|
105
|
+
</div>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
|
|
80
109
|
function renderAIIcon () {
|
|
81
110
|
return (
|
|
82
111
|
<div className='terminal-footer-unit terminal-footer-ai'>
|
|
@@ -149,7 +178,7 @@ export default auto(function FooterEntry (props) {
|
|
|
149
178
|
}
|
|
150
179
|
return (
|
|
151
180
|
<div className='terminal-footer-unit terminal-footer-info'>
|
|
152
|
-
<
|
|
181
|
+
<BarChartOutlined
|
|
153
182
|
onClick={handleInfoPanel}
|
|
154
183
|
className='pointer font14 terminal-info-icon'
|
|
155
184
|
/>
|
|
@@ -214,6 +243,7 @@ export default auto(function FooterEntry (props) {
|
|
|
214
243
|
{!isAIDisabled() && renderAIIcon()}
|
|
215
244
|
{renderCmdHistory()}
|
|
216
245
|
{renderQuickCommands()}
|
|
246
|
+
{renderTriggers()}
|
|
217
247
|
{renderBatchInputs()}
|
|
218
248
|
{renderEncodingInfo()}
|
|
219
249
|
{renderInfoIcon()}
|
|
@@ -38,6 +38,9 @@
|
|
|
38
38
|
// Q is now an icon-only button (5px internal padding); reduce left
|
|
39
39
|
// margin so the history icon stays roughly equidistant from AI and Q
|
|
40
40
|
margin-left 3px
|
|
41
|
+
.terminal-footer-triggers
|
|
42
|
+
display flex
|
|
43
|
+
align-items center
|
|
41
44
|
.batch-input-outer
|
|
42
45
|
position relative
|
|
43
46
|
.bi-full
|
|
@@ -61,4 +64,8 @@
|
|
|
61
64
|
border-radius 4px 4px 0 0
|
|
62
65
|
min-width 360px
|
|
63
66
|
.batch-input-holder
|
|
64
|
-
width 100px
|
|
67
|
+
width 100px
|
|
68
|
+
.remote-monitor-bar-on
|
|
69
|
+
.batch-input-outer.bi-show
|
|
70
|
+
.bi-full
|
|
71
|
+
bottom calc(100% + 28px)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run one command (usually picked from the command history) in several
|
|
3
|
+
* terminals at once.
|
|
4
|
+
*
|
|
5
|
+
* Reuses the footer batch input tab panel (`TabSelectList`) for picking the
|
|
6
|
+
* target terminals, and the same selection state + `batchInput` terminal API
|
|
7
|
+
* the footer batch input uses, so both entry points stay consistent.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { auto } from 'manate/react'
|
|
11
|
+
import {
|
|
12
|
+
Button,
|
|
13
|
+
Modal
|
|
14
|
+
} from 'antd'
|
|
15
|
+
import { TabSelectList } from './tab-select'
|
|
16
|
+
import { refs } from '../common/ref'
|
|
17
|
+
import {
|
|
18
|
+
terminalWebType,
|
|
19
|
+
terminalRdpType,
|
|
20
|
+
terminalVncType
|
|
21
|
+
} from '../../common/constants'
|
|
22
|
+
|
|
23
|
+
const e = window.translate
|
|
24
|
+
|
|
25
|
+
export default auto(function MultiTabRunModal (props) {
|
|
26
|
+
const { store, cmd, onClose } = props
|
|
27
|
+
const selectedTabIds = store.batchInputSelectedTabIds
|
|
28
|
+
const tabs = store.tabs.filter(tab => {
|
|
29
|
+
return tab.type !== terminalWebType &&
|
|
30
|
+
tab.type !== terminalRdpType &&
|
|
31
|
+
tab.type !== terminalVncType
|
|
32
|
+
}).sort((a, b) => {
|
|
33
|
+
// current tab goes first
|
|
34
|
+
if (a.id === store.activeTabId) return -1
|
|
35
|
+
if (b.id === store.activeTabId) return 1
|
|
36
|
+
return 0
|
|
37
|
+
})
|
|
38
|
+
const listProps = {
|
|
39
|
+
tabs,
|
|
40
|
+
activeTabId: store.activeTabId,
|
|
41
|
+
selectedTabIds,
|
|
42
|
+
onSelect: store.onSelectBatchInputSelectedTabId,
|
|
43
|
+
onSelectAll: store.selectAllBatchInputTabs,
|
|
44
|
+
onSelectNone: store.selectNoneBatchInputTabs
|
|
45
|
+
}
|
|
46
|
+
function handleRun () {
|
|
47
|
+
selectedTabIds.map(id => {
|
|
48
|
+
return refs.get('term-' + id)
|
|
49
|
+
}).forEach(term => {
|
|
50
|
+
term?.batchInput(cmd)
|
|
51
|
+
})
|
|
52
|
+
store.addCmdHistory(cmd)
|
|
53
|
+
onClose()
|
|
54
|
+
}
|
|
55
|
+
return (
|
|
56
|
+
<Modal
|
|
57
|
+
open
|
|
58
|
+
title={e('runInAllTerminals')}
|
|
59
|
+
onCancel={onClose}
|
|
60
|
+
destroyOnHidden
|
|
61
|
+
width={520}
|
|
62
|
+
footer={[
|
|
63
|
+
<Button
|
|
64
|
+
key='cancel'
|
|
65
|
+
onClick={onClose}
|
|
66
|
+
>
|
|
67
|
+
{e('cancel')}
|
|
68
|
+
</Button>,
|
|
69
|
+
<Button
|
|
70
|
+
key='run'
|
|
71
|
+
type='primary'
|
|
72
|
+
disabled={!selectedTabIds.length}
|
|
73
|
+
onClick={handleRun}
|
|
74
|
+
>
|
|
75
|
+
{e('ok')}
|
|
76
|
+
</Button>
|
|
77
|
+
]}
|
|
78
|
+
>
|
|
79
|
+
<div className='multi-tab-run-cmd'>{cmd}</div>
|
|
80
|
+
<TabSelectList {...listProps} />
|
|
81
|
+
</Modal>
|
|
82
|
+
)
|
|
83
|
+
})
|
|
@@ -6,15 +6,28 @@ import {
|
|
|
6
6
|
CodeOutlined
|
|
7
7
|
} from '@ant-design/icons'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The multi terminal selection panel: select-all/none plus one toggle button
|
|
11
|
+
* per terminal tab.
|
|
12
|
+
* Extracted so it can be reused outside the batch input popover (e.g. run a
|
|
13
|
+
* command from the history panel in several terminals at once).
|
|
14
|
+
*/
|
|
15
|
+
export function TabSelectList (props) {
|
|
16
|
+
const {
|
|
17
|
+
selectedTabIds = [],
|
|
18
|
+
tabs = [],
|
|
19
|
+
activeTabId,
|
|
20
|
+
onSelect,
|
|
21
|
+
onSelectAll,
|
|
22
|
+
onSelectNone
|
|
23
|
+
} = props
|
|
11
24
|
function renderTabs () {
|
|
12
25
|
return tabs.map(tab => {
|
|
13
26
|
const selected = selectedTabIds.includes(tab.id)
|
|
14
27
|
const itemProps = {
|
|
15
28
|
tab,
|
|
16
29
|
selected,
|
|
17
|
-
onSelect
|
|
30
|
+
onSelect,
|
|
18
31
|
id: tab.id,
|
|
19
32
|
isCurrent: tab.id === activeTabId
|
|
20
33
|
}
|
|
@@ -26,12 +39,6 @@ export default function TabSelect (props) {
|
|
|
26
39
|
)
|
|
27
40
|
})
|
|
28
41
|
}
|
|
29
|
-
function onSelectAll () {
|
|
30
|
-
window.store.selectAllBatchInputTabs()
|
|
31
|
-
}
|
|
32
|
-
function onSelectNone () {
|
|
33
|
-
window.store.selectNoneBatchInputTabs()
|
|
34
|
-
}
|
|
35
42
|
function renderBtns () {
|
|
36
43
|
return (
|
|
37
44
|
<div className='pd1t pd2b font12'>
|
|
@@ -50,17 +57,27 @@ export default function TabSelect (props) {
|
|
|
50
57
|
</div>
|
|
51
58
|
)
|
|
52
59
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
+
return (
|
|
61
|
+
<div className='pd1x alignright'>
|
|
62
|
+
{renderBtns()}
|
|
63
|
+
{renderTabs()}
|
|
64
|
+
</div>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export default function TabSelect (props) {
|
|
69
|
+
const { selectedTabIds, tabs, activeTabId } = props
|
|
70
|
+
const listProps = {
|
|
71
|
+
selectedTabIds,
|
|
72
|
+
tabs,
|
|
73
|
+
activeTabId,
|
|
74
|
+
onSelect: window.store.onSelectBatchInputSelectedTabId,
|
|
75
|
+
onSelectAll: window.store.selectAllBatchInputTabs,
|
|
76
|
+
onSelectNone: window.store.selectNoneBatchInputTabs
|
|
60
77
|
}
|
|
61
78
|
return (
|
|
62
79
|
<Popover
|
|
63
|
-
content={
|
|
80
|
+
content={<TabSelectList {...listProps} />}
|
|
64
81
|
trigger='click'
|
|
65
82
|
>
|
|
66
83
|
<span className='pointer iblock pd1x'>
|
|
@@ -5,12 +5,15 @@ import {
|
|
|
5
5
|
splitConfig,
|
|
6
6
|
quickCommandBoxHeight,
|
|
7
7
|
footerHeight,
|
|
8
|
+
remoteMonitorBarHeight,
|
|
8
9
|
shortcutBarHeight
|
|
9
10
|
} from '../../common/constants'
|
|
10
11
|
import layoutAlg from './layout-alg'
|
|
11
12
|
import calcSessionSize from './session-size-alg'
|
|
12
13
|
import TermSearch from '../terminal/term-search'
|
|
13
14
|
import Footer from '../footer/footer-entry'
|
|
15
|
+
import RemoteMonitorBar from '../remote-monitor/remote-monitor-bar-entry'
|
|
16
|
+
import { isRemoteMonitorBarVisible } from '../remote-monitor/visibility'
|
|
14
17
|
import SessionsWrap from '../session/sessions'
|
|
15
18
|
import QuickCommandsFooterBox from '../quick-commands/quick-commands-box'
|
|
16
19
|
import pixed from './pixed'
|
|
@@ -32,11 +35,10 @@ export default auto(function Layout (props) {
|
|
|
32
35
|
const {
|
|
33
36
|
width,
|
|
34
37
|
height,
|
|
38
|
+
isMobile,
|
|
35
39
|
pinnedQuickCommandBar,
|
|
36
|
-
// tabsHeight,
|
|
37
40
|
leftSidePanelWidth,
|
|
38
41
|
leftSideBarWidth,
|
|
39
|
-
// infoPanelPinned,
|
|
40
42
|
pinned,
|
|
41
43
|
rightPanelVisible,
|
|
42
44
|
rightPanelPinned,
|
|
@@ -46,47 +48,25 @@ export default auto(function Layout (props) {
|
|
|
46
48
|
shortcutBarVisible,
|
|
47
49
|
shortcutBarKbOffset
|
|
48
50
|
} = props.store
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
const
|
|
51
|
+
const monitorHeight = isRemoteMonitorBarVisible(props.store) ? remoteMonitorBarHeight : 0
|
|
52
|
+
const h = height - footerHeight - monitorHeight - (inActiveTerminal && pinnedQuickCommandBar ? quickCommandBoxHeight : 0) - (shortcutBarVisible ? shortcutBarHeight + shortcutBarKbOffset : 0) + resizeTrigger
|
|
53
|
+
const l = pinned && !isMobile ? leftSideBarWidth + leftSidePanelWidth : leftSideBarWidth
|
|
54
|
+
const r = rightPanelVisible && rightPanelPinned && !isMobile ? rightPanelWidth : 0
|
|
52
55
|
return {
|
|
53
56
|
height: h,
|
|
54
57
|
top: 0,
|
|
55
58
|
left: l,
|
|
56
|
-
width: width - l - r
|
|
59
|
+
width: Math.max(0, width - l - r)
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
const buildLayoutStyles = () => {
|
|
61
|
-
const {
|
|
62
|
-
layout,
|
|
63
|
-
height,
|
|
64
|
-
width,
|
|
65
|
-
pinnedQuickCommandBar,
|
|
66
|
-
leftSidePanelWidth,
|
|
67
|
-
leftSideBarWidth,
|
|
68
|
-
rightPanelVisible,
|
|
69
|
-
rightPanelPinned,
|
|
70
|
-
rightPanelWidth,
|
|
71
|
-
pinned,
|
|
72
|
-
shortcutBarVisible,
|
|
73
|
-
shortcutBarKbOffset
|
|
74
|
-
} = props.store
|
|
75
|
-
const l = pinned ? leftSidePanelWidth : 0
|
|
76
|
-
const r = rightPanelPinned && rightPanelVisible ? rightPanelWidth : 0
|
|
77
|
-
// account for the far-left icon bar (sidebarWidth - 1px border on desktop;
|
|
78
|
-
// 0 when the bar is hidden on mobile)
|
|
79
|
-
const w = width - l - r - (leftSideBarWidth > 0 ? leftSideBarWidth - 1 : 0)
|
|
80
|
-
const h = height - footerHeight - (pinnedQuickCommandBar ? quickCommandBoxHeight : 0) - (shortcutBarVisible ? shortcutBarHeight + shortcutBarKbOffset : 0)
|
|
81
|
-
return layoutAlg(layout, w, h)
|
|
82
|
-
}
|
|
83
63
|
const layoutSize = calcLayoutStyle()
|
|
84
64
|
const {
|
|
85
65
|
width,
|
|
86
66
|
height
|
|
87
67
|
} = layoutSize
|
|
88
68
|
const pixedLayoutStyle = pixed(layoutSize)
|
|
89
|
-
const styles =
|
|
69
|
+
const styles = layoutAlg(layout, width, height)
|
|
90
70
|
const layoutProps = {
|
|
91
71
|
layout,
|
|
92
72
|
...styles,
|
|
@@ -102,7 +82,14 @@ export default auto(function Layout (props) {
|
|
|
102
82
|
const { tabs } = store
|
|
103
83
|
const tabsBatch = {}
|
|
104
84
|
for (const tab of tabs) {
|
|
105
|
-
|
|
85
|
+
let { batch } = tab
|
|
86
|
+
// Guard against tabs with missing/invalid batch (e.g. created by
|
|
87
|
+
// MCP/AI operations before sanitization). Route them to pane 0
|
|
88
|
+
// instead of crashing or disappearing.
|
|
89
|
+
batch = Number(batch)
|
|
90
|
+
if (!Number.isInteger(batch) || batch < 0 || batch >= sizes.length) {
|
|
91
|
+
batch = 0
|
|
92
|
+
}
|
|
106
93
|
if (!tabsBatch[batch]) {
|
|
107
94
|
tabsBatch[batch] = []
|
|
108
95
|
}
|
|
@@ -207,6 +194,11 @@ export default auto(function Layout (props) {
|
|
|
207
194
|
key='QuickCommandsFooterBox'
|
|
208
195
|
{...qmProps}
|
|
209
196
|
/>,
|
|
197
|
+
<RemoteMonitorBar
|
|
198
|
+
key='RemoteMonitorBar'
|
|
199
|
+
store={store}
|
|
200
|
+
style={{ left: layoutSize.left, width, height: remoteMonitorBarHeight }}
|
|
201
|
+
/>,
|
|
210
202
|
<Footer
|
|
211
203
|
key='Footer'
|
|
212
204
|
{...footerProps}
|