@electerm/electerm-react 5.5.9 → 5.5.25
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 +12 -2
- package/client/common/constants.js +14 -0
- package/client/common/default-setting.js +2 -2
- package/client/common/download.jsx +12 -0
- package/client/common/parse-vv.js +313 -0
- package/client/common/resolve.js +20 -14
- package/client/common/theme-defaults.js +5 -2
- package/client/common/update-check.js +5 -4
- package/client/components/ai/ai-history-item.jsx +1 -1
- package/client/components/ai/ai-history.jsx +1 -0
- package/client/components/ai/ai-history.styl +23 -0
- package/client/components/ai/ai-presets.js +13 -2
- package/client/components/bg/css-overwrite.jsx +16 -1
- package/client/components/bookmark-form/common/color-picker.styl +3 -0
- package/client/components/bookmark-form/common/fields.jsx +3 -0
- package/client/components/bookmark-form/common/vv-file-field.jsx +137 -0
- package/client/components/bookmark-form/config/spice.js +1 -0
- package/client/components/common/remote-pointer.js +49 -0
- package/client/components/common/remote-session-shell.jsx +137 -0
- package/client/components/common/remote-session-shell.styl +80 -0
- package/client/components/common/responsive-tabs.jsx +89 -0
- package/client/components/common/responsive-tabs.styl +34 -0
- package/client/components/footer/footer-entry.jsx +39 -14
- package/client/components/icons/ai-icon.styl +4 -1
- package/client/components/layout/layout.jsx +7 -1
- package/client/components/main/main.jsx +9 -2
- package/client/components/main/term-fullscreen.styl +7 -0
- package/client/components/quick-commands/quick-command-ai-editor.jsx +255 -0
- package/client/components/quick-commands/quick-command-ai-editor.styl +13 -0
- package/client/components/quick-commands/quick-commands-box.jsx +23 -5
- package/client/components/quick-commands/quick-commands-form-elem.jsx +129 -65
- package/client/components/rdp/rdp-session.jsx +94 -124
- package/client/components/rdp/rdp.styl +13 -36
- package/client/components/session/session.styl +0 -16
- package/client/components/setting-panel/list.jsx +2 -2
- package/client/components/setting-panel/setting-modal.jsx +8 -3
- package/client/components/sftp/sftp-entry.jsx +26 -0
- package/client/components/side-panel-r/right-side-panel.styl +11 -1
- package/client/components/side-panel-r/side-panel-r.jsx +45 -9
- package/client/components/sidebar/bookmark-select.jsx +3 -1
- package/client/components/sidebar/index.jsx +13 -4
- package/client/components/sidebar/side-panel.jsx +8 -3
- package/client/components/sidebar/sidebar-panel.jsx +20 -7
- package/client/components/spice/spice-session.jsx +69 -72
- package/client/components/spice/spice.styl +10 -19
- package/client/components/terminal/shortcut-bar.styl +14 -0
- package/client/components/terminal/unix-timestamp-tooltip.jsx +27 -1
- package/client/components/terminal-info/terminal-info.styl +3 -0
- package/client/components/theme/terminal-theme-list.styl +3 -0
- package/client/components/theme/theme-ai-editor.jsx +2 -2
- package/client/components/theme/theme-form.jsx +20 -23
- package/client/components/theme/theme-form.styl +8 -10
- package/client/components/theme/theme-list-item.jsx +59 -25
- package/client/components/vnc/vnc-session.jsx +72 -104
- package/client/components/vnc/vnc.styl +25 -16
- package/client/css/mobile.styl +28 -0
- package/client/store/common.js +44 -0
- package/client/store/init-state.js +2 -1
- package/client/store/setting.js +4 -0
- package/client/store/sidebar.js +5 -1
- package/client/store/store.js +9 -2
- package/package.json +1 -1
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
statusMap
|
|
8
8
|
} from '../../common/constants'
|
|
9
9
|
import {
|
|
10
|
-
Spin,
|
|
11
10
|
Select
|
|
12
11
|
} from 'antd'
|
|
13
12
|
import {
|
|
@@ -18,6 +17,7 @@ import Modal from '../common/modal'
|
|
|
18
17
|
import { copy, readClipboard } from '../../common/clipboard'
|
|
19
18
|
import VncForm from './vnc-form'
|
|
20
19
|
import RemoteFloatControl from '../common/remote-float-control'
|
|
20
|
+
import RemoteSessionShell from '../common/remote-session-shell'
|
|
21
21
|
import './vnc.styl'
|
|
22
22
|
|
|
23
23
|
// noVNC module imports — loaded dynamically
|
|
@@ -71,23 +71,35 @@ export default class VncSession extends PureComponent {
|
|
|
71
71
|
})
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
remoteHeight: currentScreenData ? currentScreenData.height : null
|
|
74
|
+
// fullscreen always fills the screen, the per tab setting only applies
|
|
75
|
+
// windowed; the switch is not reachable in fullscreen anyway
|
|
76
|
+
getFit = () => {
|
|
77
|
+
if (this.props.fullscreen) {
|
|
78
|
+
return true
|
|
80
79
|
}
|
|
80
|
+
const { scaleViewport = true } = this.props.tab
|
|
81
|
+
return !!scaleViewport
|
|
81
82
|
}
|
|
82
83
|
|
|
84
|
+
// viewport box measured by the shell, used as the size hint for the server.
|
|
85
|
+
// Falls back to the layout props before the first measurement.
|
|
83
86
|
calcCanvasSize = () => {
|
|
84
|
-
const { width, height } = this.
|
|
87
|
+
const { width, height } = this.viewSize || {}
|
|
88
|
+
if (width && height) {
|
|
89
|
+
return { width, height }
|
|
90
|
+
}
|
|
91
|
+
const { width: w, height: h } = this.props
|
|
85
92
|
return {
|
|
86
|
-
width:
|
|
87
|
-
height:
|
|
93
|
+
width: w - 10,
|
|
94
|
+
height: h - 80
|
|
88
95
|
}
|
|
89
96
|
}
|
|
90
97
|
|
|
98
|
+
handleViewportResize = (width, height) => {
|
|
99
|
+
this.viewSize = { width, height }
|
|
100
|
+
this.updateViewLayout()
|
|
101
|
+
}
|
|
102
|
+
|
|
91
103
|
buildWsUrl = (port, type = 'vnc', extra = '') => {
|
|
92
104
|
const { host, tokenElecterm } = this.props.config
|
|
93
105
|
const { id } = this.props.tab
|
|
@@ -127,30 +139,6 @@ export default class VncSession extends PureComponent {
|
|
|
127
139
|
}
|
|
128
140
|
}
|
|
129
141
|
|
|
130
|
-
renderControl = () => {
|
|
131
|
-
const contrlProps = this.getControlProps({
|
|
132
|
-
fixedPosition: false,
|
|
133
|
-
showExitFullscreen: false,
|
|
134
|
-
className: 'mg1l'
|
|
135
|
-
})
|
|
136
|
-
return (
|
|
137
|
-
<div className='pd1 fix session-v-info'>
|
|
138
|
-
<div className='fleft'>
|
|
139
|
-
<ReloadOutlined
|
|
140
|
-
onClick={this.handleReInit}
|
|
141
|
-
className='mg2r mg1l pointer'
|
|
142
|
-
/>
|
|
143
|
-
{this.renderScreensSelect()}
|
|
144
|
-
{this.renderInfo()}
|
|
145
|
-
</div>
|
|
146
|
-
<div className='fright'>
|
|
147
|
-
{this.props.fullscreenIcon()}
|
|
148
|
-
<RemoteFloatControl {...contrlProps} />
|
|
149
|
-
</div>
|
|
150
|
-
</div>
|
|
151
|
-
)
|
|
152
|
-
}
|
|
153
|
-
|
|
154
142
|
renderScreensSelect = () => {
|
|
155
143
|
const {
|
|
156
144
|
screens,
|
|
@@ -232,8 +220,9 @@ export default class VncSession extends PureComponent {
|
|
|
232
220
|
|
|
233
221
|
const { width, height } = this.calcCanvasSize()
|
|
234
222
|
const wsUrl = this.buildWsUrl(port, 'vnc', `&width=${width}&height=${height}`)
|
|
235
|
-
//
|
|
236
|
-
//
|
|
223
|
+
// noVNC sizes its own canvas (`_screen` fills whatever box it is mounted
|
|
224
|
+
// into, the canvas is centered in it), so scaling is entirely driven by
|
|
225
|
+
// `scaleViewport` plus the size of the mount node.
|
|
237
226
|
const vncOpts = {
|
|
238
227
|
clipViewport,
|
|
239
228
|
scaleViewport,
|
|
@@ -241,13 +230,6 @@ export default class VncSession extends PureComponent {
|
|
|
241
230
|
qualityLevel, // JPEG quality 0-9, lower = faster
|
|
242
231
|
compressionLevel, // Compression 0-9, lower = faster
|
|
243
232
|
shared, // Allow shared connections
|
|
244
|
-
style: scaleViewport
|
|
245
|
-
? {
|
|
246
|
-
width: width + 'px',
|
|
247
|
-
height: height + 'px',
|
|
248
|
-
overflow: 'hidden'
|
|
249
|
-
}
|
|
250
|
-
: {},
|
|
251
233
|
credentials: {}
|
|
252
234
|
}
|
|
253
235
|
if (username) {
|
|
@@ -378,7 +360,7 @@ export default class VncSession extends PureComponent {
|
|
|
378
360
|
for (const event of events) {
|
|
379
361
|
rfb.addEventListener(event, this[`on${window.capitalizeFirstLetter(event)}`])
|
|
380
362
|
}
|
|
381
|
-
rfb.scaleViewport =
|
|
363
|
+
rfb.scaleViewport = this.getFit()
|
|
382
364
|
rfb.clipViewport = clipViewport
|
|
383
365
|
rfb.qualityLevel = qualityLevel
|
|
384
366
|
rfb.compressionLevel = compressionLevel
|
|
@@ -550,9 +532,10 @@ export default class VncSession extends PureComponent {
|
|
|
550
532
|
}
|
|
551
533
|
|
|
552
534
|
componentDidUpdate (prevProps, prevState) {
|
|
535
|
+
// viewport size changes are reported by the shell, the layout props are
|
|
536
|
+
// stale in fullscreen so they are not used here
|
|
553
537
|
if (
|
|
554
|
-
prevProps.
|
|
555
|
-
prevProps.height !== this.props.height ||
|
|
538
|
+
prevProps.fullscreen !== this.props.fullscreen ||
|
|
556
539
|
prevState.currentScreen !== this.state.currentScreen
|
|
557
540
|
) {
|
|
558
541
|
this.updateViewLayout()
|
|
@@ -561,24 +544,25 @@ export default class VncSession extends PureComponent {
|
|
|
561
544
|
|
|
562
545
|
updateViewLayout = () => {
|
|
563
546
|
const { currentScreen, screens } = this.state
|
|
564
|
-
const {
|
|
547
|
+
const { clipViewport = false } = this.props.tab
|
|
565
548
|
const container = this.domRef.current ? this.domRef.current.firstElementChild : null
|
|
566
549
|
|
|
567
550
|
if (!this.rfb || !container) {
|
|
568
551
|
return
|
|
569
552
|
}
|
|
570
553
|
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
|
|
554
|
+
// keep noVNC scaling in sync with our fit state, then re-run autoscale so
|
|
555
|
+
// our patched single screen logic sees the new viewport size
|
|
556
|
+
const fit = this.getFit()
|
|
557
|
+
this.rfb.scaleViewport = fit
|
|
574
558
|
this.rfb.clipViewport = clipViewport
|
|
575
559
|
|
|
576
|
-
|
|
577
|
-
if (scaleViewport && this.rfb._display) {
|
|
560
|
+
if (fit && this.rfb._display) {
|
|
578
561
|
this.rfb._display.autoscale(container.clientWidth, container.clientHeight)
|
|
579
562
|
}
|
|
580
563
|
|
|
581
564
|
// Handle scrolling to the selected screen
|
|
565
|
+
clearTimeout(this.timer)
|
|
582
566
|
this.timer = setTimeout(() => {
|
|
583
567
|
if (currentScreen === 'all') {
|
|
584
568
|
container.scrollTo(0, 0)
|
|
@@ -653,61 +637,45 @@ export default class VncSession extends PureComponent {
|
|
|
653
637
|
}
|
|
654
638
|
|
|
655
639
|
render () {
|
|
656
|
-
const { width: w, height: h } = this.props
|
|
657
640
|
const { loading } = this.state
|
|
658
|
-
const
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
className: 'vnc-session-wrap session-v-wrap'
|
|
681
|
-
}
|
|
682
|
-
const contrlProps = this.getControlProps()
|
|
683
|
-
const sessProps = {
|
|
684
|
-
className: 'vnc-session-wrap',
|
|
685
|
-
style: {
|
|
686
|
-
width: w + 'px',
|
|
687
|
-
height: h + 'px'
|
|
688
|
-
}
|
|
689
|
-
}
|
|
641
|
+
const floatProps = this.getControlProps()
|
|
642
|
+
const inlineProps = this.getControlProps({
|
|
643
|
+
fixedPosition: false,
|
|
644
|
+
showExitFullscreen: false,
|
|
645
|
+
className: 'mg1l'
|
|
646
|
+
})
|
|
647
|
+
const controlLeft = (
|
|
648
|
+
<>
|
|
649
|
+
<ReloadOutlined
|
|
650
|
+
onClick={this.handleReInit}
|
|
651
|
+
className='mg2r mg1l pointer'
|
|
652
|
+
/>
|
|
653
|
+
{this.renderScreensSelect()}
|
|
654
|
+
{this.renderInfo()}
|
|
655
|
+
</>
|
|
656
|
+
)
|
|
657
|
+
const controlRight = (
|
|
658
|
+
<>
|
|
659
|
+
{this.props.fullscreenIcon()}
|
|
660
|
+
<RemoteFloatControl {...inlineProps} />
|
|
661
|
+
</>
|
|
662
|
+
)
|
|
690
663
|
return (
|
|
691
|
-
<
|
|
664
|
+
<RemoteSessionShell
|
|
665
|
+
loading={loading}
|
|
666
|
+
fit={this.getFit()}
|
|
667
|
+
wrapClassName='vnc-session-wrap'
|
|
668
|
+
onViewportResize={this.handleViewportResize}
|
|
669
|
+
controlLeft={controlLeft}
|
|
670
|
+
controlRight={controlRight}
|
|
671
|
+
floatControl={<RemoteFloatControl {...floatProps} />}
|
|
672
|
+
overlay={this.renderConfirm()}
|
|
673
|
+
>
|
|
692
674
|
<div
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
{...contrlProps}
|
|
698
|
-
/>
|
|
699
|
-
<div
|
|
700
|
-
style={wrapperStyle}
|
|
701
|
-
className='vnc-scroll-wrapper'
|
|
702
|
-
>
|
|
703
|
-
<div
|
|
704
|
-
{...divProps}
|
|
705
|
-
ref={this.domRef}
|
|
706
|
-
/>
|
|
707
|
-
</div>
|
|
708
|
-
{this.renderConfirm()}
|
|
709
|
-
</div>
|
|
710
|
-
</Spin>
|
|
675
|
+
className='remote-session-fill vnc-session-mount'
|
|
676
|
+
ref={this.domRef}
|
|
677
|
+
/>
|
|
678
|
+
</RemoteSessionShell>
|
|
711
679
|
)
|
|
712
680
|
}
|
|
713
681
|
}
|
|
@@ -1,17 +1,26 @@
|
|
|
1
|
+
// VNC only styling — the session layout (control bar, viewport, fit mode)
|
|
2
|
+
// lives in the shared remote session shell: common/remote-session-shell.styl
|
|
1
3
|
.vnc-session-wrap
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
4
|
+
.vnc-session-mount
|
|
5
|
+
// noVNC mounts its own screen element in here and sizes its canvas
|
|
6
|
+
// against this box, so it has to keep a definite size
|
|
7
|
+
width 100%
|
|
8
|
+
height 100%
|
|
9
|
+
// noVNC's screen element carries no class of its own: letterbox area
|
|
10
|
+
// follows the app theme instead of noVNC's own background
|
|
11
|
+
> div
|
|
12
|
+
background transparent !important
|
|
13
|
+
// scrolled when fit is off and the remote screen is bigger than
|
|
14
|
+
// the window
|
|
15
|
+
&::-webkit-scrollbar
|
|
16
|
+
width 16px
|
|
17
|
+
height 16px
|
|
18
|
+
background var(--main-darker)
|
|
19
|
+
&::-webkit-scrollbar-track
|
|
20
|
+
background var(--main-darker)
|
|
21
|
+
box-shadow inset 0 0 5px var(--main-darker)
|
|
22
|
+
&::-webkit-scrollbar-thumb
|
|
23
|
+
background var(--primary)
|
|
24
|
+
border-radius 0
|
|
25
|
+
&::-webkit-scrollbar-corner
|
|
26
|
+
background var(--main-darker)
|
package/client/css/mobile.styl
CHANGED
|
@@ -11,6 +11,34 @@
|
|
|
11
11
|
.remote-monitor-bar
|
|
12
12
|
position fixed
|
|
13
13
|
|
|
14
|
+
// Side panels are full-screen drawers on mobile: they start at the viewport
|
|
15
|
+
// edge (the left one covers the icon bar too, so no strip of terminal is left
|
|
16
|
+
// showing) and run to the bottom edge — footer included. On desktop the
|
|
17
|
+
// footer is reserved *beside* the panel because it holds the panel's own
|
|
18
|
+
// trigger; on mobile the drawer is dismissed from its header (✕ / the
|
|
19
|
+
// bookmark/history tabs) instead, so there is nothing to keep clear. The
|
|
20
|
+
// inline width already comes from store.leftSidePanelWidth / rightPanelWidth,
|
|
21
|
+
// which are the viewport width here.
|
|
22
|
+
// Both are on the fixed frame (the right panel explicitly: its bottom edge
|
|
23
|
+
// must not drift from the fixed footer when the visual viewport changes), and
|
|
24
|
+
// both have to outrank the footer's 200 — it comes later in the DOM, so at an
|
|
25
|
+
// equal rank it would paint over the drawer. The left drawer lives inside
|
|
26
|
+
// .sidebar, which is its own stacking context (z-index 100), so lifting the
|
|
27
|
+
// drawer alone is not enough: the whole sidebar column has to clear the
|
|
28
|
+
// footer. The right panel is a root-level child, so its own z-index suffices.
|
|
29
|
+
// The icon bar takes no extra room from this: it is 43px wide and the footer
|
|
30
|
+
// starts at 44px.
|
|
31
|
+
.sidebar
|
|
32
|
+
z-index 250
|
|
33
|
+
.sidebar-list
|
|
34
|
+
left 0
|
|
35
|
+
bottom 0
|
|
36
|
+
z-index 250
|
|
37
|
+
.right-side-panel
|
|
38
|
+
position fixed
|
|
39
|
+
bottom 0
|
|
40
|
+
z-index 250
|
|
41
|
+
|
|
14
42
|
// ---- 1. Tabs: show only current tab + dropdown, close icon always visible
|
|
15
43
|
.tabs.mobile-tabs
|
|
16
44
|
display flex
|
package/client/store/common.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
leftSidePanelWidthKey,
|
|
11
11
|
leftSideBarOpenKey,
|
|
12
12
|
rightSidebarWidthKey,
|
|
13
|
+
rightPanelPinnedKey,
|
|
13
14
|
addPanelWidthLsKey,
|
|
14
15
|
connectionMap,
|
|
15
16
|
lastAiChatSessionIdKey,
|
|
@@ -42,6 +43,18 @@ export default Store => {
|
|
|
42
43
|
window.store.setConfig(ext)
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
// The footer info icon is a toggle: the same click opens and closes the
|
|
47
|
+
// panel, so the trigger never has to be hunted down in the panel header.
|
|
48
|
+
Store.prototype.toggleInfoPanel = action(function () {
|
|
49
|
+
const { store } = window
|
|
50
|
+
const isOpen = store.rightPanelVisible && store.rightPanelTab === 'info'
|
|
51
|
+
store.rightPanelVisible = !isOpen
|
|
52
|
+
store.rightPanelTab = 'info'
|
|
53
|
+
if (!isOpen) {
|
|
54
|
+
store.openInfoPanelAction()
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
45
58
|
Store.prototype.openInfoPanel = action(function () {
|
|
46
59
|
const { store } = window
|
|
47
60
|
store.rightPanelVisible = true
|
|
@@ -122,7 +135,13 @@ export default Store => {
|
|
|
122
135
|
Object.assign(window.store._termSearchOptions, update)
|
|
123
136
|
}
|
|
124
137
|
|
|
138
|
+
// Both panel widths are desktop-only preferences: on mobile the panel is a
|
|
139
|
+
// full-width drawer whose width is fixed, so a drag (or any other caller)
|
|
140
|
+
// must not overwrite the desktop value that is still in localStorage.
|
|
125
141
|
Store.prototype.setLeftSidePanelWidth = function (v) {
|
|
142
|
+
if (window.store.isMobile) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
126
145
|
ls.setItem(leftSidePanelWidthKey, v)
|
|
127
146
|
window.store._leftSidePanelWidth = v
|
|
128
147
|
}
|
|
@@ -144,9 +163,22 @@ export default Store => {
|
|
|
144
163
|
}
|
|
145
164
|
|
|
146
165
|
Store.prototype.setRightSidePanelWidth = function (v) {
|
|
166
|
+
if (window.store.isMobile) {
|
|
167
|
+
return
|
|
168
|
+
}
|
|
147
169
|
ls.setItem(rightSidebarWidthKey, v)
|
|
148
170
|
window.store._rightPanelWidth = v
|
|
149
171
|
}
|
|
172
|
+
|
|
173
|
+
// Persist the pin the same way the left sidebar does (sidebarPinnedKey):
|
|
174
|
+
// pinned is a durable layout preference, not a per-session toggle. It matters
|
|
175
|
+
// more here because pinned and unpinned are visibly different modes — the
|
|
176
|
+
// unpinned panel is an overlay that clears the footer, the pinned one is a
|
|
177
|
+
// full-height dock.
|
|
178
|
+
Store.prototype.setRightPanelPinned = function (v) {
|
|
179
|
+
ls.setItem(rightPanelPinnedKey, v + '')
|
|
180
|
+
window.store.rightPanelPinned = v
|
|
181
|
+
}
|
|
150
182
|
Store.prototype.beforeExit = function (evt) {
|
|
151
183
|
const { confirmBeforeExit } = window.store.config
|
|
152
184
|
if (
|
|
@@ -286,6 +318,18 @@ export default Store => {
|
|
|
286
318
|
}
|
|
287
319
|
}
|
|
288
320
|
|
|
321
|
+
// Same toggle contract as toggleInfoPanel: the footer AI button opens and
|
|
322
|
+
// closes its own panel. The open path must go through handleOpenAIPanel so
|
|
323
|
+
// the missing-config prompt still fires; closing must not touch config.
|
|
324
|
+
Store.prototype.toggleAIPanel = function () {
|
|
325
|
+
const { store } = window
|
|
326
|
+
if (store.rightPanelVisible && store.rightPanelTab === 'ai') {
|
|
327
|
+
store.rightPanelVisible = false
|
|
328
|
+
return
|
|
329
|
+
}
|
|
330
|
+
store.handleOpenAIPanel()
|
|
331
|
+
}
|
|
332
|
+
|
|
289
333
|
Store.prototype.explainWithAi = function (txt) {
|
|
290
334
|
const { store } = window
|
|
291
335
|
store.handleOpenAIPanel()
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
localAddrBookmarkLsKey,
|
|
19
19
|
leftSidePanelWidthKey,
|
|
20
20
|
rightSidebarWidthKey,
|
|
21
|
+
rightPanelPinnedKey,
|
|
21
22
|
addPanelWidthLsKey,
|
|
22
23
|
qmSortByFrequencyKey,
|
|
23
24
|
resolutionsLsKey,
|
|
@@ -125,7 +126,7 @@ export default () => {
|
|
|
125
126
|
terminalInfoProps: {},
|
|
126
127
|
rightPanelVisible: false,
|
|
127
128
|
rightPanelTab: 'info',
|
|
128
|
-
rightPanelPinned:
|
|
129
|
+
rightPanelPinned: ls.getItem(rightPanelPinnedKey) === 'true',
|
|
129
130
|
_rightPanelWidth: parseInt(ls.getItem(rightSidebarWidthKey), 10) || 500,
|
|
130
131
|
showAIConfigModal: false,
|
|
131
132
|
|
package/client/store/setting.js
CHANGED
|
@@ -111,6 +111,10 @@ export default Store => {
|
|
|
111
111
|
})
|
|
112
112
|
store.setSettingItem(settingList().find(d => d.id === settingSyncId))
|
|
113
113
|
store.openSettingModal()
|
|
114
|
+
// on mobile, jump straight to the sync form instead of the menu list
|
|
115
|
+
if (store.isMobile) {
|
|
116
|
+
store.settingMobileView = 'content'
|
|
117
|
+
}
|
|
114
118
|
})
|
|
115
119
|
}
|
|
116
120
|
|
package/client/store/sidebar.js
CHANGED
|
@@ -25,7 +25,11 @@ export default Store => {
|
|
|
25
25
|
// through this one method.
|
|
26
26
|
Store.prototype.openLeftSidePanel = function (field = 'bookmarks') {
|
|
27
27
|
const { store } = window
|
|
28
|
-
|
|
28
|
+
// A pinned panel is already open, so there is nothing to toggle — except
|
|
29
|
+
// on mobile, where pinning is not a reachable mode (no pin control) and
|
|
30
|
+
// the panel is a full-width overlay. Honouring a stale desktop pin here
|
|
31
|
+
// would leave the bookmark button dead on arrival.
|
|
32
|
+
if (store.pinned && !store.isMobile) {
|
|
29
33
|
return
|
|
30
34
|
}
|
|
31
35
|
store.setOpenedSideBar(store.openedSideBar ? '' : field)
|
package/client/store/store.js
CHANGED
|
@@ -61,10 +61,15 @@ class Store {
|
|
|
61
61
|
return window.store.innerWidth
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
// Mobile turns a side panel into a full-screen drawer: it spans the whole
|
|
65
|
+
// viewport and its width is not user-adjustable. So the stored desktop width
|
|
66
|
+
// is ignored here, and so is the icon bar (the drawer covers it) — and the
|
|
67
|
+
// old 500px cap, which left a strip of terminal showing on windows between
|
|
68
|
+
// 500 and 600px, i.e. the top of the mobile range.
|
|
64
69
|
get leftSidePanelWidth () {
|
|
65
70
|
const { store } = window
|
|
66
71
|
if (store.isMobile) {
|
|
67
|
-
return
|
|
72
|
+
return store.innerWidth
|
|
68
73
|
}
|
|
69
74
|
return store._leftSidePanelWidth
|
|
70
75
|
}
|
|
@@ -78,10 +83,12 @@ class Store {
|
|
|
78
83
|
return store._leftSideBarOpen ? sidebarWidth : 0
|
|
79
84
|
}
|
|
80
85
|
|
|
86
|
+
// Same as leftSidePanelWidth: full-screen drawer on mobile, width not
|
|
87
|
+
// user-adjustable.
|
|
81
88
|
get rightPanelWidth () {
|
|
82
89
|
const { store } = window
|
|
83
90
|
if (store.isMobile) {
|
|
84
|
-
return
|
|
91
|
+
return store.innerWidth
|
|
85
92
|
}
|
|
86
93
|
return store._rightPanelWidth
|
|
87
94
|
}
|