@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
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { lazy, Suspense } from 'react'
|
|
2
|
+
import { auto } from 'manate/react'
|
|
3
|
+
import { isRemoteMonitorBarVisible } from './visibility'
|
|
4
|
+
import LazyBoundary from '../common/lazy-boundary'
|
|
5
|
+
|
|
6
|
+
const RemoteMonitorBar = lazy(() => import('./remote-monitor-bar'))
|
|
7
|
+
|
|
8
|
+
export default auto(function RemoteMonitorBarEntry (props) {
|
|
9
|
+
if (!isRemoteMonitorBarVisible(props.store)) return null
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<LazyBoundary>
|
|
13
|
+
<Suspense fallback={null}>
|
|
14
|
+
<RemoteMonitorBar {...props} />
|
|
15
|
+
</Suspense>
|
|
16
|
+
</LazyBoundary>
|
|
17
|
+
)
|
|
18
|
+
})
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { auto } from 'manate/react'
|
|
2
|
+
import { Button, Popover } from 'antd'
|
|
3
|
+
import {
|
|
4
|
+
CloseOutlined,
|
|
5
|
+
WarningOutlined
|
|
6
|
+
} from '@ant-design/icons'
|
|
7
|
+
import { useEffect, useMemo, useState } from 'react'
|
|
8
|
+
import { statusMap } from '../../common/constants'
|
|
9
|
+
import {
|
|
10
|
+
formatBytes,
|
|
11
|
+
groupOf,
|
|
12
|
+
sortDisks,
|
|
13
|
+
MONITOR_ITEM_GROUP,
|
|
14
|
+
formatRate,
|
|
15
|
+
normalizeRemoteMonitorItems,
|
|
16
|
+
REMOTE_MONITOR_ITEM_IDS,
|
|
17
|
+
selectPrimaryNetwork
|
|
18
|
+
} from './monitor-model'
|
|
19
|
+
import { useMonitorDetails } from './use-monitor-details'
|
|
20
|
+
import MonitorDetails, { Sparkline } from './monitor-details'
|
|
21
|
+
import { getRemoteMonitorTab, isRemoteMonitorBarVisible } from './visibility'
|
|
22
|
+
import { runCmd } from '../terminal/terminal-apis'
|
|
23
|
+
import ItemFilter from '../common/item-filter'
|
|
24
|
+
import './remote-monitor-bar.styl'
|
|
25
|
+
|
|
26
|
+
const e = window.translate
|
|
27
|
+
|
|
28
|
+
function severity (level) {
|
|
29
|
+
return {
|
|
30
|
+
unknown: -1,
|
|
31
|
+
normal: 0,
|
|
32
|
+
warning: 1,
|
|
33
|
+
critical: 2
|
|
34
|
+
}[level] ?? -1
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function combineLevel (levels) {
|
|
38
|
+
return levels.reduce((current, level) => {
|
|
39
|
+
return severity(level) > severity(current) ? level : current
|
|
40
|
+
}, 'unknown')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function statusMarker (level) {
|
|
44
|
+
if (level !== 'warning' && level !== 'critical') {
|
|
45
|
+
return null
|
|
46
|
+
}
|
|
47
|
+
return (
|
|
48
|
+
<WarningOutlined
|
|
49
|
+
aria-hidden='true'
|
|
50
|
+
className={`remote-monitor-status-marker remote-monitor-level-${level}`}
|
|
51
|
+
/>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function statusText (level) {
|
|
56
|
+
if (level === 'critical') return e('critical')
|
|
57
|
+
if (level === 'warning') return e('warning')
|
|
58
|
+
if (level === 'unknown') return e('unknown')
|
|
59
|
+
return e('normal')
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function compactDuration (seconds) {
|
|
63
|
+
const value = Math.max(0, Math.floor(seconds))
|
|
64
|
+
const days = Math.floor(value / 86400)
|
|
65
|
+
const hours = Math.floor((value % 86400) / 3600)
|
|
66
|
+
const minutes = Math.floor((value % 3600) / 60)
|
|
67
|
+
const secs = value % 60
|
|
68
|
+
if (days) return `${days}d ${String(hours).padStart(2, '0')}h`
|
|
69
|
+
if (hours) return `${hours}h ${String(minutes).padStart(2, '0')}m`
|
|
70
|
+
if (minutes) return `${minutes}m ${String(secs).padStart(2, '0')}s`
|
|
71
|
+
return `${secs}s`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function summaryFor (id, snapshot, levels) {
|
|
75
|
+
if (id === 'hostname') {
|
|
76
|
+
return groupOf(snapshot, 'sysinfo').data?.hostname || '—'
|
|
77
|
+
}
|
|
78
|
+
if (id === 'cpu') {
|
|
79
|
+
const value = groupOf(snapshot, 'cpu').data
|
|
80
|
+
return `CPU ${Number.isFinite(value) ? `${Math.round(value)}%` : '—'}`
|
|
81
|
+
}
|
|
82
|
+
if (id === 'cpuHistory') {
|
|
83
|
+
return <Sparkline history={snapshot.cpuHistory} level={levels.cpu} />
|
|
84
|
+
}
|
|
85
|
+
if (id === 'memory') {
|
|
86
|
+
const memory = groupOf(snapshot, 'memory').data
|
|
87
|
+
return memory
|
|
88
|
+
? `Mem ${formatBytes(memory.usedBytes)} / ${formatBytes(memory.totalBytes)}`
|
|
89
|
+
: 'Mem —'
|
|
90
|
+
}
|
|
91
|
+
if (id === 'upload' || id === 'download') {
|
|
92
|
+
const network = groupOf(snapshot, 'network').data
|
|
93
|
+
const primary = selectPrimaryNetwork(network)
|
|
94
|
+
const rate = id === 'upload' ? primary?.txRate : primary?.rxRate
|
|
95
|
+
return `${id === 'upload' ? '↑' : '↓'} ${formatRate(rate)}`
|
|
96
|
+
}
|
|
97
|
+
if (id === 'uptime') {
|
|
98
|
+
const uptime = groupOf(snapshot, 'uptime').data
|
|
99
|
+
return `Up ${uptime ? compactDuration(uptime.seconds) : '—'}`
|
|
100
|
+
}
|
|
101
|
+
if (id === 'users') {
|
|
102
|
+
const users = groupOf(snapshot, 'users').data?.users
|
|
103
|
+
if (!users) return 'Users —'
|
|
104
|
+
if (!users.length) return 'Users 0'
|
|
105
|
+
return users.length > 1 ? `${users[0]} +${users.length - 1}` : users[0]
|
|
106
|
+
}
|
|
107
|
+
if (id === 'disks') {
|
|
108
|
+
const disks = sortDisks(groupOf(snapshot, 'disks').data)
|
|
109
|
+
if (!disks.length) return 'Disk —'
|
|
110
|
+
const shown = disks.slice(0, 3)
|
|
111
|
+
const groupReady = groupOf(snapshot, 'disks').status === 'ready'
|
|
112
|
+
return (
|
|
113
|
+
<span className='remote-monitor-disk-summary'>
|
|
114
|
+
{
|
|
115
|
+
shown.map(disk => {
|
|
116
|
+
const level = groupReady
|
|
117
|
+
? levels.disks[disk.mount] || 'unknown'
|
|
118
|
+
: 'unknown'
|
|
119
|
+
return (
|
|
120
|
+
<span
|
|
121
|
+
className={`remote-monitor-disk-value remote-monitor-level-${level}`}
|
|
122
|
+
key={disk.mount}
|
|
123
|
+
>
|
|
124
|
+
{disk.mount}:{disk.percent}%
|
|
125
|
+
</span>
|
|
126
|
+
)
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
{disks.length > shown.length ? <span>+{disks.length - shown.length}</span> : null}
|
|
130
|
+
</span>
|
|
131
|
+
)
|
|
132
|
+
}
|
|
133
|
+
return '—'
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function accessibleSummaryFor (id, summary, snapshot) {
|
|
137
|
+
if (typeof summary === 'string' || typeof summary === 'number') {
|
|
138
|
+
return String(summary)
|
|
139
|
+
}
|
|
140
|
+
if (id === 'cpuHistory') {
|
|
141
|
+
const latest = snapshot.cpuHistory[snapshot.cpuHistory.length - 1]?.value
|
|
142
|
+
return Number.isFinite(latest) ? `${Math.round(latest)}%` : '—'
|
|
143
|
+
}
|
|
144
|
+
if (id === 'disks') {
|
|
145
|
+
const disks = sortDisks(groupOf(snapshot, 'disks').data)
|
|
146
|
+
if (!disks.length) return '—'
|
|
147
|
+
const shown = disks.slice(0, 3).map(disk => `${disk.mount}: ${disk.percent}%`)
|
|
148
|
+
return `${shown.join(', ')}${disks.length > shown.length ? `, +${disks.length - shown.length}` : ''}`
|
|
149
|
+
}
|
|
150
|
+
return '—'
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function levelForItem (id, levels, snapshot) {
|
|
154
|
+
const group = groupOf(snapshot, MONITOR_ITEM_GROUP[id])
|
|
155
|
+
if (group.status !== 'ready') {
|
|
156
|
+
return 'unknown'
|
|
157
|
+
}
|
|
158
|
+
if (id === 'cpu' || id === 'cpuHistory') return levels.cpu
|
|
159
|
+
if (id === 'memory') return levels.memory
|
|
160
|
+
if (id === 'disks') return combineLevel(Object.values(levels.disks))
|
|
161
|
+
if (id === 'upload' || id === 'download') {
|
|
162
|
+
const primary = selectPrimaryNetwork(group.data)
|
|
163
|
+
const rate = id === 'upload' ? primary?.txRate : primary?.rxRate
|
|
164
|
+
return Number.isFinite(rate) ? 'normal' : 'unknown'
|
|
165
|
+
}
|
|
166
|
+
return group.data === null || group.data === undefined ? 'unknown' : 'normal'
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export default auto(function RemoteMonitorBar ({ store, style }) {
|
|
170
|
+
const visible = isRemoteMonitorBarVisible(store)
|
|
171
|
+
const config = store.config
|
|
172
|
+
const tab = getRemoteMonitorTab(store) || {}
|
|
173
|
+
const itemsKey = JSON.stringify(config.remoteMonitorBarItems)
|
|
174
|
+
const items = useMemo(
|
|
175
|
+
() => normalizeRemoteMonitorItems(config.remoteMonitorBarItems),
|
|
176
|
+
[itemsKey]
|
|
177
|
+
)
|
|
178
|
+
const enabledItems = items.filter(item => item.enabled)
|
|
179
|
+
const [openId, setOpenId] = useState(null)
|
|
180
|
+
const [pinnedId, setPinnedId] = useState(null)
|
|
181
|
+
const [filterOpen, setFilterOpen] = useState(false)
|
|
182
|
+
// Touch has no hover, so the details popover there is click-driven: a tap
|
|
183
|
+
// toggles it and a tap outside closes it (store.isTouchDevice follows the
|
|
184
|
+
// input the user actually uses, see main.jsx). The hover/pin bookkeeping
|
|
185
|
+
// below is mouse-only.
|
|
186
|
+
const touchMode = !!store.isTouchDevice
|
|
187
|
+
const connected = visible && tab.status === statusMap.success
|
|
188
|
+
const requestedItems = enabledItems.map(item => item.id)
|
|
189
|
+
if (openId === 'cpu' || openId === 'memory') requestedItems.push('activities')
|
|
190
|
+
const { snapshot, levels } = useMonitorDetails(tab.id || '', requestedItems, connected)
|
|
191
|
+
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
setOpenId(null)
|
|
194
|
+
setPinnedId(null)
|
|
195
|
+
}, [tab.id, itemsKey, visible])
|
|
196
|
+
|
|
197
|
+
// keep the controls revealed while the filter popover is open, the popover
|
|
198
|
+
// lives in a portal so hovering it does not keep the bar hovered
|
|
199
|
+
useEffect(() => {
|
|
200
|
+
setFilterOpen(false)
|
|
201
|
+
}, [tab.id, visible])
|
|
202
|
+
|
|
203
|
+
if (!visible) {
|
|
204
|
+
return null
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function handleOpenChange (id, open) {
|
|
208
|
+
if (touchMode) {
|
|
209
|
+
// the popover click trigger owns open/close
|
|
210
|
+
if (open) {
|
|
211
|
+
setOpenId(id)
|
|
212
|
+
} else {
|
|
213
|
+
closePopover()
|
|
214
|
+
}
|
|
215
|
+
return
|
|
216
|
+
}
|
|
217
|
+
if (open) {
|
|
218
|
+
setOpenId(id)
|
|
219
|
+
if (pinnedId && pinnedId !== id) {
|
|
220
|
+
setPinnedId(null)
|
|
221
|
+
}
|
|
222
|
+
} else if (pinnedId !== id) {
|
|
223
|
+
setOpenId(current => current === id ? null : current)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function handleClick (id) {
|
|
228
|
+
if (touchMode) {
|
|
229
|
+
// click trigger already toggled it, don't toggle twice
|
|
230
|
+
return
|
|
231
|
+
}
|
|
232
|
+
if (pinnedId === id) {
|
|
233
|
+
setPinnedId(null)
|
|
234
|
+
setOpenId(null)
|
|
235
|
+
} else {
|
|
236
|
+
setPinnedId(id)
|
|
237
|
+
setOpenId(id)
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function closePopover () {
|
|
242
|
+
setPinnedId(null)
|
|
243
|
+
setOpenId(null)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function toggleItem (id) {
|
|
247
|
+
const next = items.map(item => (
|
|
248
|
+
item.id === id ? { ...item, enabled: !item.enabled } : item
|
|
249
|
+
))
|
|
250
|
+
store.setConfig({
|
|
251
|
+
// keep the stored shape in sync with the setting panel: enabled ids only
|
|
252
|
+
remoteMonitorBarItems: next.filter(item => item.enabled).map(item => item.id)
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const connectionMessage = tab.status === statusMap.error
|
|
257
|
+
? e('disconnected')
|
|
258
|
+
: e('loading')
|
|
259
|
+
|
|
260
|
+
return (
|
|
261
|
+
<div
|
|
262
|
+
aria-label={e('remoteMonitorBar')}
|
|
263
|
+
className={'remote-monitor-bar' + (filterOpen ? ' remote-monitor-bar-filter-open' : '')}
|
|
264
|
+
onKeyDown={event => {
|
|
265
|
+
if (event.key === 'Escape') {
|
|
266
|
+
closePopover()
|
|
267
|
+
}
|
|
268
|
+
}}
|
|
269
|
+
role='region'
|
|
270
|
+
style={style}
|
|
271
|
+
>
|
|
272
|
+
<div className='remote-monitor-scroll'>
|
|
273
|
+
{
|
|
274
|
+
connected
|
|
275
|
+
? enabledItems.length
|
|
276
|
+
? enabledItems.map(item => {
|
|
277
|
+
const level = levelForItem(item.id, levels, snapshot)
|
|
278
|
+
const classLevel = item.id === 'disks' && level !== 'unknown'
|
|
279
|
+
? 'normal'
|
|
280
|
+
: level
|
|
281
|
+
const label = e(item.id)
|
|
282
|
+
const summary = summaryFor(item.id, snapshot, levels)
|
|
283
|
+
const accessibleSummary = accessibleSummaryFor(item.id, summary, snapshot)
|
|
284
|
+
return (
|
|
285
|
+
<Popover
|
|
286
|
+
autoAdjustOverflow
|
|
287
|
+
content={(
|
|
288
|
+
<MonitorDetails
|
|
289
|
+
config={config}
|
|
290
|
+
id={item.id}
|
|
291
|
+
levels={levels}
|
|
292
|
+
onClose={closePopover}
|
|
293
|
+
onKillProcess={pid => runCmd(tab.id, `kill ${pid}`)}
|
|
294
|
+
snapshot={snapshot}
|
|
295
|
+
tab={tab}
|
|
296
|
+
/>
|
|
297
|
+
)}
|
|
298
|
+
key={item.id}
|
|
299
|
+
mouseEnterDelay={0.2}
|
|
300
|
+
mouseLeaveDelay={0.15}
|
|
301
|
+
onOpenChange={open => handleOpenChange(item.id, open)}
|
|
302
|
+
open={openId === item.id}
|
|
303
|
+
placement='top'
|
|
304
|
+
trigger={touchMode ? 'click' : ['hover', 'focus']}
|
|
305
|
+
>
|
|
306
|
+
<button
|
|
307
|
+
aria-label={`${label}: ${accessibleSummary}; ${statusText(level)}`}
|
|
308
|
+
className={`remote-monitor-item remote-monitor-item-${item.id} remote-monitor-level-${classLevel}`}
|
|
309
|
+
data-monitor-item={item.id}
|
|
310
|
+
onClick={() => handleClick(item.id)}
|
|
311
|
+
title={label}
|
|
312
|
+
type='button'
|
|
313
|
+
>
|
|
314
|
+
{statusMarker(level)}
|
|
315
|
+
{summary}
|
|
316
|
+
</button>
|
|
317
|
+
</Popover>
|
|
318
|
+
)
|
|
319
|
+
})
|
|
320
|
+
: <div className='remote-monitor-message'>{e('noItems')}</div>
|
|
321
|
+
: <div className='remote-monitor-message'>{connectionMessage}</div>
|
|
322
|
+
}
|
|
323
|
+
</div>
|
|
324
|
+
<div className='remote-monitor-controls'>
|
|
325
|
+
<ItemFilter
|
|
326
|
+
className='remote-monitor-filter'
|
|
327
|
+
ids={REMOTE_MONITOR_ITEM_IDS}
|
|
328
|
+
onOpenChange={setFilterOpen}
|
|
329
|
+
onToggle={toggleItem}
|
|
330
|
+
placement='topRight'
|
|
331
|
+
selected={enabledItems.map(item => item.id)}
|
|
332
|
+
/>
|
|
333
|
+
<Button
|
|
334
|
+
aria-label={e('close')}
|
|
335
|
+
icon={<CloseOutlined />}
|
|
336
|
+
onClick={() => store.setConfig({ remoteMonitorBarEnabled: false })}
|
|
337
|
+
size='small'
|
|
338
|
+
title={e('close')}
|
|
339
|
+
type='text'
|
|
340
|
+
/>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
)
|
|
344
|
+
})
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
.remote-monitor-bar
|
|
2
|
+
position absolute
|
|
3
|
+
bottom 36px
|
|
4
|
+
z-index 188
|
|
5
|
+
overflow hidden
|
|
6
|
+
display flex
|
|
7
|
+
background var(--main)
|
|
8
|
+
color var(--text)
|
|
9
|
+
font-size 12px
|
|
10
|
+
font-variant-numeric tabular-nums
|
|
11
|
+
user-select none
|
|
12
|
+
|
|
13
|
+
.remote-monitor-scroll
|
|
14
|
+
min-width 0
|
|
15
|
+
flex 1
|
|
16
|
+
display flex
|
|
17
|
+
overflow-x auto
|
|
18
|
+
overflow-y hidden
|
|
19
|
+
scrollbar-width none // Firefox
|
|
20
|
+
-ms-overflow-style none // IE/Edge
|
|
21
|
+
overscroll-behavior-x contain
|
|
22
|
+
&::-webkit-scrollbar
|
|
23
|
+
width 0
|
|
24
|
+
height 0
|
|
25
|
+
display none
|
|
26
|
+
|
|
27
|
+
.remote-monitor-item
|
|
28
|
+
height 100%
|
|
29
|
+
flex-shrink 0
|
|
30
|
+
display inline-flex
|
|
31
|
+
align-items center
|
|
32
|
+
gap 4px
|
|
33
|
+
padding 0 9px
|
|
34
|
+
border 0
|
|
35
|
+
outline none
|
|
36
|
+
background transparent
|
|
37
|
+
color var(--text)
|
|
38
|
+
font inherit
|
|
39
|
+
white-space nowrap
|
|
40
|
+
cursor default
|
|
41
|
+
transition color 120ms ease, background-color 120ms ease
|
|
42
|
+
&:hover,
|
|
43
|
+
&:focus-visible
|
|
44
|
+
background var(--main-light)
|
|
45
|
+
&:focus-visible
|
|
46
|
+
box-shadow inset 0 0 0 1px var(--primary)
|
|
47
|
+
|
|
48
|
+
.remote-monitor-item-hostname
|
|
49
|
+
display block
|
|
50
|
+
max-width 220px
|
|
51
|
+
overflow hidden
|
|
52
|
+
text-overflow ellipsis
|
|
53
|
+
|
|
54
|
+
.remote-monitor-disk-summary
|
|
55
|
+
display inline-flex
|
|
56
|
+
align-items center
|
|
57
|
+
gap 6px
|
|
58
|
+
|
|
59
|
+
.remote-monitor-status-marker
|
|
60
|
+
flex-shrink 0
|
|
61
|
+
font-size 10px
|
|
62
|
+
|
|
63
|
+
.remote-monitor-message
|
|
64
|
+
display flex
|
|
65
|
+
align-items center
|
|
66
|
+
min-width max-content
|
|
67
|
+
padding 0 10px
|
|
68
|
+
color var(--text-disabled)
|
|
69
|
+
font-variant-numeric tabular-nums
|
|
70
|
+
|
|
71
|
+
.remote-monitor-controls
|
|
72
|
+
flex-shrink 0
|
|
73
|
+
display flex
|
|
74
|
+
align-items center
|
|
75
|
+
opacity 0
|
|
76
|
+
pointer-events none
|
|
77
|
+
.ant-btn
|
|
78
|
+
width 28px
|
|
79
|
+
height 100%
|
|
80
|
+
padding 0
|
|
81
|
+
color var(--text-dark)
|
|
82
|
+
&:hover,
|
|
83
|
+
&:focus-visible
|
|
84
|
+
color var(--text)
|
|
85
|
+
background var(--main-light)
|
|
86
|
+
.item-filter
|
|
87
|
+
height 100%
|
|
88
|
+
padding 0 6px
|
|
89
|
+
&:hover
|
|
90
|
+
background var(--main-light)
|
|
91
|
+
&:focus-visible
|
|
92
|
+
outline none
|
|
93
|
+
box-shadow inset 0 0 0 1px var(--primary)
|
|
94
|
+
|
|
95
|
+
.remote-monitor-bar:hover,
|
|
96
|
+
.remote-monitor-bar:focus-within,
|
|
97
|
+
.remote-monitor-bar-filter-open,
|
|
98
|
+
.is-mobile,
|
|
99
|
+
.is-touch-device
|
|
100
|
+
.remote-monitor-controls
|
|
101
|
+
opacity 1
|
|
102
|
+
pointer-events auto
|
|
103
|
+
|
|
104
|
+
@media (prefers-reduced-motion: reduce)
|
|
105
|
+
.remote-monitor-item
|
|
106
|
+
transition none
|