@electerm/electerm-react 5.3.26 → 5.5.6
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 +14 -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/footer-entry.jsx +35 -5
- package/client/components/footer/footer.styl +8 -1
- 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/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 +7 -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/side-panel-r/side-panel-r.jsx +2 -2
- 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 +88 -0
- package/client/components/terminal/mixins/term-context-menu.jsx +291 -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 +136 -0
- package/client/components/terminal/shell-detect.js +37 -0
- package/client/components/terminal/shell.js +0 -28
- package/client/components/terminal/shortcut-bar-entry.jsx +6 -3
- package/client/components/terminal/shortcut-bar.jsx +4 -6
- package/client/components/terminal/shortcut-bar.styl +4 -0
- package/client/components/terminal/ssh-reload-state.js +56 -0
- package/client/components/terminal/startup-queue.js +379 -0
- package/client/components/terminal/terminal-interactive.jsx +32 -0
- package/client/components/terminal/terminal.jsx +102 -2007
- 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 +9 -1
- 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 +41 -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
|
@@ -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)
|
|
@@ -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}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { auto } from 'manate/react'
|
|
2
|
-
import { useEffect } from 'react'
|
|
2
|
+
import { lazy, Suspense, useEffect } from 'react'
|
|
3
3
|
import Layout from '../layout/layout'
|
|
4
4
|
import FileInfoModal from '../sftp/file-info-modal'
|
|
5
5
|
import FileCompareModal from '../sftp/file-compare-modal'
|
|
@@ -10,7 +10,6 @@ import Sidebar from '../sidebar'
|
|
|
10
10
|
import CssOverwrite from '../bg/css-overwrite'
|
|
11
11
|
import UiTheme from './ui-theme'
|
|
12
12
|
import CustomCss from '../bg/custom-css.jsx'
|
|
13
|
-
import Resolutions from '../rdp/resolution-edit'
|
|
14
13
|
import TerminalInteractive from '../terminal/terminal-interactive'
|
|
15
14
|
import ConfirmModalStore from '../file-transfer/conflict-resolve.jsx'
|
|
16
15
|
import TransferQueue from '../file-transfer/transfer-queue'
|
|
@@ -19,18 +18,21 @@ import TerminalCmdSuggestions from '../terminal/terminal-command-dropdown'
|
|
|
19
18
|
import TransportsActionStore from '../file-transfer/transports-action-store.jsx'
|
|
20
19
|
import classnames from 'classnames'
|
|
21
20
|
import ShortcutControl from '../shortcuts/shortcut-control.jsx'
|
|
22
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
footerHeight,
|
|
23
|
+
isMac,
|
|
24
|
+
isWin,
|
|
25
|
+
remoteMonitorBarHeight,
|
|
26
|
+
textTerminalBgValue
|
|
27
|
+
} from '../../common/constants'
|
|
23
28
|
import { isAIDisabled } from '../../common/ai-feature'
|
|
24
29
|
import { ConfigProvider } from 'antd'
|
|
25
30
|
import { NotificationContainer } from '../common/notification'
|
|
26
|
-
import InfoModal from '../sidebar/info-modal.jsx'
|
|
27
31
|
import RightSidePanel from '../side-panel-r/side-panel-r'
|
|
28
32
|
import ConnectionHoppingWarning from './connection-hopping-warnning'
|
|
29
33
|
import SshConfigLoadNotify from '../ssh-config/ssh-config-load-notify'
|
|
30
34
|
import LoadSshConfigs from '../ssh-config/load-ssh-configs'
|
|
31
35
|
import AIChat from '../ai/ai-chat-entry'
|
|
32
|
-
import AIConfigModal from '../ai/ai-config-modal'
|
|
33
|
-
import Opacity from '../common/opacity'
|
|
34
36
|
import MoveItemModal from '../tree-list/move-item-modal'
|
|
35
37
|
import InputContextMenu from '../common/input-context-menu'
|
|
36
38
|
import WorkspaceSaveModal from '../tabs/workspace-save-modal'
|
|
@@ -44,9 +46,17 @@ import deepCopy from 'json-deep-copy'
|
|
|
44
46
|
import './wrapper.styl'
|
|
45
47
|
import TerminalInfo from '../terminal-info/terminal-info-entry'
|
|
46
48
|
import ShortcutBarEntry from '../terminal/shortcut-bar-entry'
|
|
49
|
+
import LazyBoundary from '../common/lazy-boundary'
|
|
50
|
+
import { isRemoteMonitorBarVisible } from '../remote-monitor/visibility'
|
|
47
51
|
import '../../common/fs.js'
|
|
48
52
|
import './term-fullscreen.styl'
|
|
49
53
|
|
|
54
|
+
const Resolutions = lazy(() => import('../rdp/resolution-edit'))
|
|
55
|
+
const InfoModal = lazy(() => import('../sidebar/info-modal.jsx'))
|
|
56
|
+
const AIConfigModal = lazy(() => import('../ai/ai-config-modal'))
|
|
57
|
+
// window opacity is electron-only: lazy so the web app never loads the chunk
|
|
58
|
+
const Opacity = lazy(() => import('../common/opacity'))
|
|
59
|
+
|
|
50
60
|
export default auto(function Index (props) {
|
|
51
61
|
useEffect(() => {
|
|
52
62
|
const { store } = props
|
|
@@ -129,6 +139,7 @@ export default auto(function Index (props) {
|
|
|
129
139
|
widgetInstances
|
|
130
140
|
} = store
|
|
131
141
|
const upgradeInfo = deepCopy(store.upgradeInfo)
|
|
142
|
+
const remoteMonitorBarVisible = isRemoteMonitorBarVisible(store)
|
|
132
143
|
const cls = classnames({
|
|
133
144
|
loaded: configLoaded,
|
|
134
145
|
'not-webapp': !window.et.isWebApp,
|
|
@@ -147,12 +158,14 @@ export default auto(function Index (props) {
|
|
|
147
158
|
'is-main': !isSecondInstance,
|
|
148
159
|
'is-mobile': store.isMobile,
|
|
149
160
|
'is-desktop': !store.isMobile,
|
|
150
|
-
'is-touch-device': store.isTouchDevice
|
|
161
|
+
'is-touch-device': store.isTouchDevice,
|
|
162
|
+
'remote-monitor-bar-on': remoteMonitorBarVisible
|
|
151
163
|
})
|
|
152
164
|
const ext1 = {
|
|
153
165
|
className: cls,
|
|
154
166
|
style: {
|
|
155
|
-
'--left-side-bar-width': store.leftSideBarWidth + 'px'
|
|
167
|
+
'--left-side-bar-width': store.leftSideBarWidth + 'px',
|
|
168
|
+
'--footer-stack-height': `${footerHeight + (remoteMonitorBarVisible ? remoteMonitorBarHeight : 0)}px`
|
|
156
169
|
}
|
|
157
170
|
}
|
|
158
171
|
// Get active tab IDs
|
|
@@ -242,20 +255,6 @@ export default auto(function Index (props) {
|
|
|
242
255
|
title: rightPanelTitle,
|
|
243
256
|
rightPanelTab
|
|
244
257
|
}
|
|
245
|
-
const terminalInfoProps = {
|
|
246
|
-
rightPanelTab,
|
|
247
|
-
...deepCopy(store.terminalInfoProps),
|
|
248
|
-
...pick(
|
|
249
|
-
config,
|
|
250
|
-
[
|
|
251
|
-
'host',
|
|
252
|
-
'port',
|
|
253
|
-
'saveTerminalLogToFile',
|
|
254
|
-
'terminalInfos',
|
|
255
|
-
'sessionLogPath'
|
|
256
|
-
]
|
|
257
|
-
)
|
|
258
|
-
}
|
|
259
258
|
const sshConfigProps = {
|
|
260
259
|
...pick(store, [
|
|
261
260
|
'settingTab',
|
|
@@ -293,13 +292,23 @@ export default auto(function Index (props) {
|
|
|
293
292
|
{...confsCss}
|
|
294
293
|
configLoaded={configLoaded}
|
|
295
294
|
/>
|
|
296
|
-
|
|
295
|
+
{window.et.isWebApp
|
|
296
|
+
? null
|
|
297
|
+
: (
|
|
298
|
+
<LazyBoundary>
|
|
299
|
+
<Suspense fallback={null}>
|
|
300
|
+
<Opacity opacity={config.opacity} />
|
|
301
|
+
</Suspense>
|
|
302
|
+
</LazyBoundary>
|
|
303
|
+
)}
|
|
297
304
|
<TerminalInteractive />
|
|
298
305
|
<UiTheme
|
|
299
306
|
{...themeProps}
|
|
300
307
|
/>
|
|
301
308
|
<CustomCss customCss={config.customCss} configLoaded={configLoaded} />
|
|
302
|
-
|
|
309
|
+
{store.textEditorRequested && (
|
|
310
|
+
<TextEditor />
|
|
311
|
+
)}
|
|
303
312
|
<UpdateCheck
|
|
304
313
|
skipVersion={config.skipVersion}
|
|
305
314
|
upgradeInfo={upgradeInfo}
|
|
@@ -325,11 +334,23 @@ export default auto(function Index (props) {
|
|
|
325
334
|
config={config}
|
|
326
335
|
/>
|
|
327
336
|
<Remote2RemoteHandlers />
|
|
328
|
-
|
|
329
|
-
|
|
337
|
+
{openResolutionEdit && (
|
|
338
|
+
<LazyBoundary>
|
|
339
|
+
<Suspense fallback={null}>
|
|
340
|
+
<Resolutions {...resProps} />
|
|
341
|
+
</Suspense>
|
|
342
|
+
</LazyBoundary>
|
|
343
|
+
)}
|
|
344
|
+
{store.showInfoModal && (
|
|
345
|
+
<LazyBoundary>
|
|
346
|
+
<Suspense fallback={null}>
|
|
347
|
+
<InfoModal {...infoModalProps} />
|
|
348
|
+
</Suspense>
|
|
349
|
+
</LazyBoundary>
|
|
350
|
+
)}
|
|
330
351
|
<RightSidePanel {...rightPanelProps}>
|
|
331
352
|
{!isAIDisabled() && <AIChat {...aiChatProps} />}
|
|
332
|
-
<TerminalInfo key={store.activeTabId} {...terminalInfoProps} />
|
|
353
|
+
<TerminalInfo key={store.activeTabId} store={store} {...deepCopy(store.terminalInfoProps)} />
|
|
333
354
|
</RightSidePanel>
|
|
334
355
|
<SshConfigLoadNotify {...sshConfigProps} />
|
|
335
356
|
<LoadSshConfigs
|
|
@@ -346,7 +367,13 @@ export default auto(function Index (props) {
|
|
|
346
367
|
<BatchOpRunner />
|
|
347
368
|
<ImportProgress />
|
|
348
369
|
<ShortcutBarEntry store={store} />
|
|
349
|
-
{!isAIDisabled() &&
|
|
370
|
+
{!isAIDisabled() && store.showAIConfigModal && (
|
|
371
|
+
<LazyBoundary>
|
|
372
|
+
<Suspense fallback={null}>
|
|
373
|
+
<AIConfigModal store={store} />
|
|
374
|
+
</Suspense>
|
|
375
|
+
</LazyBoundary>
|
|
376
|
+
)}
|
|
350
377
|
<UnixTimestampTooltip />
|
|
351
378
|
</div>
|
|
352
379
|
</ConfigProvider>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
.terminal-control
|
|
6
6
|
.qm-wrap-tooltip
|
|
7
7
|
.main-footer
|
|
8
|
+
.remote-monitor-bar
|
|
8
9
|
.session-v-info
|
|
9
10
|
display none
|
|
10
11
|
// Mobile tabs carry the extra .mobile-tabs class, and mobile.styl uses
|
|
@@ -40,11 +41,16 @@
|
|
|
40
41
|
// visible; the session shrinks to sit on top of the footer. RDP/VNC/Spice
|
|
41
42
|
// fullscreen stays footer-less — inActiveTerminal is false for those tabs, so
|
|
42
43
|
// the fs-with-footer class is absent there.
|
|
43
|
-
// 36px
|
|
44
|
+
// --footer-stack-height includes the 36px footer and the optional monitor row.
|
|
44
45
|
.fullscreen.fs-with-footer
|
|
45
46
|
.main-footer
|
|
46
47
|
display block
|
|
47
48
|
left 0 !important
|
|
49
|
+
&.remote-monitor-bar-on
|
|
50
|
+
.remote-monitor-bar
|
|
51
|
+
display flex
|
|
52
|
+
left 0 !important
|
|
53
|
+
width 100% !important
|
|
48
54
|
.qm-wrap-tooltip
|
|
49
55
|
display block
|
|
50
56
|
left 0 !important
|
|
@@ -53,10 +59,10 @@
|
|
|
53
59
|
display none
|
|
54
60
|
.session-wrap.session-current
|
|
55
61
|
.term-wrap
|
|
56
|
-
height calc(100% - 36px - var(--shortcut-bar-h, 0px)) !important
|
|
62
|
+
height calc(100% - var(--footer-stack-height, 36px) - var(--shortcut-bar-h, 0px) - var(--shortcut-bar-kb-offset, 0px)) !important
|
|
57
63
|
// pinned quick-command bar (180px, same as quickCommandBoxHeight) sits
|
|
58
64
|
// between terminal and footer — reserve its space too
|
|
59
65
|
&.qm-pinned
|
|
60
66
|
.session-wrap.session-current
|
|
61
67
|
.term-wrap
|
|
62
|
-
height calc(100% - 36px - 180px - var(--shortcut-bar-h, 0px)) !important
|
|
68
|
+
height calc(100% - var(--footer-stack-height, 36px) - 180px - var(--shortcut-bar-h, 0px) - var(--shortcut-bar-kb-offset, 0px)) !important
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
right 0
|
|
10
10
|
z-index 89
|
|
11
11
|
height auto
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
bottom var(--footer-stack-height, 36px)
|
|
13
14
|
|
|
14
15
|
.qm-pinned
|
|
15
16
|
.qm-wrap-tooltip
|
|
@@ -101,4 +102,4 @@
|
|
|
101
102
|
> .drag
|
|
102
103
|
align-self flex-start
|
|
103
104
|
> button:last-child
|
|
104
|
-
align-self flex-end
|
|
105
|
+
align-self flex-end
|
|
@@ -7,6 +7,7 @@ import { getLocalFileInfo } from '../sftp/file-read'
|
|
|
7
7
|
import { osResolve } from '../../common/resolve'
|
|
8
8
|
import { filesize } from 'filesize'
|
|
9
9
|
import sanitizeFilename from '../../common/sanitize-filename'
|
|
10
|
+
import { chooseSaveDirectory } from '../../common/choose-save-folder'
|
|
10
11
|
|
|
11
12
|
const LOG_PREFIX = '[RDP-FILE-TRANSFER]'
|
|
12
13
|
|
|
@@ -231,23 +232,22 @@ export class FileTransferManager {
|
|
|
231
232
|
|
|
232
233
|
async handleFileDownload (fileInfo) {
|
|
233
234
|
try {
|
|
234
|
-
const savePath = await
|
|
235
|
+
const savePath = await chooseSaveDirectory({
|
|
235
236
|
title: `Save ${fileInfo.name}`,
|
|
236
237
|
message: `Choose where to save ${fileInfo.name}`,
|
|
237
|
-
properties: ['openDirectory', 'createDirectory'],
|
|
238
238
|
noBrowserTransfer: true
|
|
239
239
|
}).catch((err) => {
|
|
240
240
|
this.log(`Save dialog error: ${err.message}`, 'error')
|
|
241
241
|
return false
|
|
242
242
|
})
|
|
243
243
|
|
|
244
|
-
if (!savePath
|
|
244
|
+
if (!savePath) {
|
|
245
245
|
this.log('Download cancelled by user', 'info')
|
|
246
246
|
return
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
const safeName = sanitizeFilename(fileInfo.name)
|
|
250
|
-
const fullPath = osResolve(savePath
|
|
250
|
+
const fullPath = osResolve(savePath, safeName)
|
|
251
251
|
|
|
252
252
|
const fd = await new Promise((resolve, reject) => {
|
|
253
253
|
window.fs.open(fullPath, O_WRONLY | O_CREAT | O_TRUNC, (err, fd) => {
|