@electerm/electerm-react 5.3.15 → 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 +23 -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/bookmark-form/index.jsx +10 -8
- 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 +80 -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/col.jsx +32 -6
- package/client/components/setting-panel/list.styl +12 -0
- package/client/components/setting-panel/setting-common.jsx +25 -56
- package/client/components/setting-panel/setting-mobile-breadcrumb.jsx +29 -0
- package/client/components/setting-panel/setting-modal.jsx +64 -44
- package/client/components/setting-panel/setting-terminal.jsx +7 -2
- package/client/components/setting-panel/setting-wrap.jsx +0 -3
- package/client/components/setting-panel/setting-wrap.styl +80 -11
- 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/sidebar/index.jsx +28 -21
- package/client/components/sidebar/left-sidebar-icons.jsx +1 -1
- package/client/components/sidebar/sidebar.styl +16 -0
- 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/tabs/tabs.styl +4 -0
- 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 +32 -27
- 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/theme/theme-form.jsx +22 -0
- package/client/components/tree-list/bookmark-transport.jsx +7 -31
- package/client/components/tree-list/tree-list.styl +13 -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 +14 -0
- package/client/store/mcp-handler.js +158 -30
- package/client/store/setting.js +20 -0
- package/client/store/sidebar.js +12 -0
- package/client/store/store.js +2 -0
- package/client/store/sync.js +45 -9
- package/client/store/system-menu.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
|
|
@@ -86,6 +96,30 @@ export default auto(function Index (props) {
|
|
|
86
96
|
store.checkPendingDeepLink()
|
|
87
97
|
}, [])
|
|
88
98
|
|
|
99
|
+
// Track the actual input modality rather than a static capability probe:
|
|
100
|
+
// a touch-capable laptop is a mouse machine until a real touch happens,
|
|
101
|
+
// and a tablet stays touch even though it can also pair a mouse. Stored in
|
|
102
|
+
// `store.isTouchDevice` (seeded false — no upfront probe, which only says
|
|
103
|
+
// the screen *can* be touched, not what the user operates with); the
|
|
104
|
+
// `is-touch-device` class drives always-visible hover-only action icons, so
|
|
105
|
+
// it must follow the device the user is actually operating.
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
const { store } = window
|
|
108
|
+
const handlePointer = (e) => {
|
|
109
|
+
if (e.pointerType === 'mouse') {
|
|
110
|
+
store.isTouchDevice = false
|
|
111
|
+
} else if (e.pointerType === 'touch' || e.pointerType === 'pen') {
|
|
112
|
+
store.isTouchDevice = true
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
document.addEventListener('pointerdown', handlePointer)
|
|
116
|
+
document.addEventListener('pointermove', handlePointer)
|
|
117
|
+
return () => {
|
|
118
|
+
document.removeEventListener('pointerdown', handlePointer)
|
|
119
|
+
document.removeEventListener('pointermove', handlePointer)
|
|
120
|
+
}
|
|
121
|
+
}, [])
|
|
122
|
+
|
|
89
123
|
const { store } = props
|
|
90
124
|
const {
|
|
91
125
|
configLoaded,
|
|
@@ -105,6 +139,7 @@ export default auto(function Index (props) {
|
|
|
105
139
|
widgetInstances
|
|
106
140
|
} = store
|
|
107
141
|
const upgradeInfo = deepCopy(store.upgradeInfo)
|
|
142
|
+
const remoteMonitorBarVisible = isRemoteMonitorBarVisible(store)
|
|
108
143
|
const cls = classnames({
|
|
109
144
|
loaded: configLoaded,
|
|
110
145
|
'not-webapp': !window.et.isWebApp,
|
|
@@ -122,12 +157,15 @@ export default auto(function Index (props) {
|
|
|
122
157
|
'fs-with-footer': fullscreen && store.inActiveTerminal,
|
|
123
158
|
'is-main': !isSecondInstance,
|
|
124
159
|
'is-mobile': store.isMobile,
|
|
125
|
-
'is-desktop': !store.isMobile
|
|
160
|
+
'is-desktop': !store.isMobile,
|
|
161
|
+
'is-touch-device': store.isTouchDevice,
|
|
162
|
+
'remote-monitor-bar-on': remoteMonitorBarVisible
|
|
126
163
|
})
|
|
127
164
|
const ext1 = {
|
|
128
165
|
className: cls,
|
|
129
166
|
style: {
|
|
130
|
-
'--left-side-bar-width': store.leftSideBarWidth + 'px'
|
|
167
|
+
'--left-side-bar-width': store.leftSideBarWidth + 'px',
|
|
168
|
+
'--footer-stack-height': `${footerHeight + (remoteMonitorBarVisible ? remoteMonitorBarHeight : 0)}px`
|
|
131
169
|
}
|
|
132
170
|
}
|
|
133
171
|
// Get active tab IDs
|
|
@@ -217,20 +255,6 @@ export default auto(function Index (props) {
|
|
|
217
255
|
title: rightPanelTitle,
|
|
218
256
|
rightPanelTab
|
|
219
257
|
}
|
|
220
|
-
const terminalInfoProps = {
|
|
221
|
-
rightPanelTab,
|
|
222
|
-
...deepCopy(store.terminalInfoProps),
|
|
223
|
-
...pick(
|
|
224
|
-
config,
|
|
225
|
-
[
|
|
226
|
-
'host',
|
|
227
|
-
'port',
|
|
228
|
-
'saveTerminalLogToFile',
|
|
229
|
-
'terminalInfos',
|
|
230
|
-
'sessionLogPath'
|
|
231
|
-
]
|
|
232
|
-
)
|
|
233
|
-
}
|
|
234
258
|
const sshConfigProps = {
|
|
235
259
|
...pick(store, [
|
|
236
260
|
'settingTab',
|
|
@@ -268,13 +292,23 @@ export default auto(function Index (props) {
|
|
|
268
292
|
{...confsCss}
|
|
269
293
|
configLoaded={configLoaded}
|
|
270
294
|
/>
|
|
271
|
-
|
|
295
|
+
{window.et.isWebApp
|
|
296
|
+
? null
|
|
297
|
+
: (
|
|
298
|
+
<LazyBoundary>
|
|
299
|
+
<Suspense fallback={null}>
|
|
300
|
+
<Opacity opacity={config.opacity} />
|
|
301
|
+
</Suspense>
|
|
302
|
+
</LazyBoundary>
|
|
303
|
+
)}
|
|
272
304
|
<TerminalInteractive />
|
|
273
305
|
<UiTheme
|
|
274
306
|
{...themeProps}
|
|
275
307
|
/>
|
|
276
308
|
<CustomCss customCss={config.customCss} configLoaded={configLoaded} />
|
|
277
|
-
|
|
309
|
+
{store.textEditorRequested && (
|
|
310
|
+
<TextEditor />
|
|
311
|
+
)}
|
|
278
312
|
<UpdateCheck
|
|
279
313
|
skipVersion={config.skipVersion}
|
|
280
314
|
upgradeInfo={upgradeInfo}
|
|
@@ -300,11 +334,23 @@ export default auto(function Index (props) {
|
|
|
300
334
|
config={config}
|
|
301
335
|
/>
|
|
302
336
|
<Remote2RemoteHandlers />
|
|
303
|
-
|
|
304
|
-
|
|
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
|
+
)}
|
|
305
351
|
<RightSidePanel {...rightPanelProps}>
|
|
306
352
|
{!isAIDisabled() && <AIChat {...aiChatProps} />}
|
|
307
|
-
<TerminalInfo key={store.activeTabId} {...terminalInfoProps} />
|
|
353
|
+
<TerminalInfo key={store.activeTabId} store={store} {...deepCopy(store.terminalInfoProps)} />
|
|
308
354
|
</RightSidePanel>
|
|
309
355
|
<SshConfigLoadNotify {...sshConfigProps} />
|
|
310
356
|
<LoadSshConfigs
|
|
@@ -321,7 +367,13 @@ export default auto(function Index (props) {
|
|
|
321
367
|
<BatchOpRunner />
|
|
322
368
|
<ImportProgress />
|
|
323
369
|
<ShortcutBarEntry store={store} />
|
|
324
|
-
{!isAIDisabled() &&
|
|
370
|
+
{!isAIDisabled() && store.showAIConfigModal && (
|
|
371
|
+
<LazyBoundary>
|
|
372
|
+
<Suspense fallback={null}>
|
|
373
|
+
<AIConfigModal store={store} />
|
|
374
|
+
</Suspense>
|
|
375
|
+
</LazyBoundary>
|
|
376
|
+
)}
|
|
325
377
|
<UnixTimestampTooltip />
|
|
326
378
|
</div>
|
|
327
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) => {
|