@electerm/electerm-react 5.0.6 → 5.0.8
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/client/common/constants.js +2 -1
- package/client/common/fetch-from-server.js +12 -2
- package/client/components/common/drawer.styl +1 -1
- package/client/components/common/logo-elem.jsx +1 -1
- package/client/components/common/logo.styl +8 -2
- package/client/components/footer/cmd-history.jsx +13 -7
- package/client/components/footer/cmd-history.styl +8 -0
- package/client/components/footer/footer-entry.jsx +24 -4
- package/client/components/footer/footer.styl +1 -1
- package/client/components/layout/layout.jsx +13 -8
- package/client/components/main/main.jsx +6 -2
- package/client/components/quick-commands/qm.styl +1 -1
- package/client/components/quick-commands/quick-commands-box.jsx +5 -21
- package/client/components/quick-commands/quick-commands-select.jsx +10 -20
- package/client/components/session/session.jsx +1 -1
- package/client/components/session/sessions.jsx +1 -1
- package/client/components/setting-panel/setting-modal.jsx +1 -1
- package/client/components/setting-panel/setting-wrap.jsx +1 -2
- package/client/components/setting-panel/setting-wrap.styl +1 -1
- package/client/components/setting-panel/setting.styl +1 -1
- package/client/components/sidebar/bookmark-select.jsx +2 -2
- package/client/components/sidebar/history.jsx +13 -10
- package/client/components/sidebar/index.jsx +23 -7
- package/client/components/sidebar/side-panel.jsx +3 -3
- package/client/components/sidebar/sidebar.styl +12 -1
- package/client/components/tabs/no-session.jsx +1 -1
- package/client/components/tabs/no-session.styl +31 -8
- package/client/components/terminal/shell.js +10 -2
- package/client/components/terminal/terminal-apis.js +4 -4
- package/client/components/terminal/terminal-command-dropdown.jsx +1 -0
- package/client/components/terminal/terminal.jsx +50 -17
- package/client/components/tree-list/tree-list-row.jsx +2 -2
- package/client/components/tree-list/tree-list.jsx +1 -1
- package/client/css/mobile.styl +20 -7
- package/client/store/common.js +15 -3
- package/client/store/init-state.js +9 -2
- package/client/store/store.js +12 -3
- package/package.json +1 -1
|
@@ -147,8 +147,9 @@ export const terminalFtpType = 'ftp'
|
|
|
147
147
|
export const terminalSpiceType = 'spice'
|
|
148
148
|
export const openedSidebarKey = 'opened-sidebar'
|
|
149
149
|
export const sidebarPinnedKey = 'sidebar-pinned'
|
|
150
|
+
export const leftSideBarOpenKey = 'left-side-bar-open'
|
|
150
151
|
export const pinnedQuickCommandBarKey = 'pinned-quick-command-bar'
|
|
151
|
-
export const
|
|
152
|
+
export const leftSidePanelWidthKey = 'left-sidebar-width'
|
|
152
153
|
export const rightSidebarWidthKey = 'right-sidebar-width'
|
|
153
154
|
export const addPanelWidthLsKey = 'addPanelWidth'
|
|
154
155
|
export const sftpDefaultSortSettingKey = 'sftp-default-sort'
|
|
@@ -27,7 +27,8 @@ export const initWsCommon = async () => {
|
|
|
27
27
|
|
|
28
28
|
window.pre.ipcOnEvent('power-resume', initWsCommon)
|
|
29
29
|
|
|
30
|
-
const wsFetch = async (data) => {
|
|
30
|
+
const wsFetch = async (data, options = {}) => {
|
|
31
|
+
const { silent = false } = options
|
|
31
32
|
if (!window.et.wsOpened) {
|
|
32
33
|
await initWsCommon()
|
|
33
34
|
}
|
|
@@ -35,7 +36,16 @@ const wsFetch = async (data) => {
|
|
|
35
36
|
return new NewPromise((resolve, reject) => {
|
|
36
37
|
window.et.commonWs.once((arg) => {
|
|
37
38
|
if (arg.error) {
|
|
38
|
-
|
|
39
|
+
// Rejections are propagated to the caller via the returned promise, so
|
|
40
|
+
// callers decide severity. Many fetches (shell detection, owner lookup,
|
|
41
|
+
// folder size) are best-effort and catch gracefully; logging every one
|
|
42
|
+
// at error level with a stack dump surfaces scary-but-harmless failures
|
|
43
|
+
// (e.g. an SSH server rejecting an auxiliary exec channel). Default to a
|
|
44
|
+
// low-key warning carrying only the message; best-effort probes pass
|
|
45
|
+
// { silent: true } to suppress this and log their own clearer message.
|
|
46
|
+
if (!silent) {
|
|
47
|
+
console.warn('fetch error:', arg.error.message || arg.error)
|
|
48
|
+
}
|
|
39
49
|
return reject(new Error(arg.error.message))
|
|
40
50
|
}
|
|
41
51
|
resolve(arg.data)
|
|
@@ -9,7 +9,7 @@ import './logo.styl'
|
|
|
9
9
|
|
|
10
10
|
export default function LogoElem () {
|
|
11
11
|
return (
|
|
12
|
-
<h1 className='mg3y font50'>
|
|
12
|
+
<h1 className='logo-elem mg3y font50'>
|
|
13
13
|
<span className='iblock pd3 morph-shape mg1l mg1r'>
|
|
14
14
|
<img src={logoPath2} className='iblock mwm-100 mg1l mg1r logo-filter logo-img' />
|
|
15
15
|
</span>
|
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
.logo-img-small
|
|
4
4
|
height 28px
|
|
5
5
|
border-radius 28px
|
|
6
|
-
|
|
6
|
+
// the h1 vertical margin so the logo stops dominating the no-session panel.
|
|
7
|
+
.is-mobile
|
|
8
|
+
.logo-elem
|
|
9
|
+
margin-top 8px
|
|
10
|
+
margin-bottom 8px
|
|
11
|
+
.morph-shape
|
|
12
|
+
padding 6px
|
|
7
13
|
.logo-img
|
|
8
|
-
height
|
|
14
|
+
height 32px
|
|
9
15
|
.logo-img-small
|
|
10
16
|
height 20px
|
|
@@ -111,13 +111,19 @@ export default auto(function CmdHistory (props) {
|
|
|
111
111
|
}
|
|
112
112
|
return (
|
|
113
113
|
<div className='cmd-history-header pd2b'>
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
114
|
+
<div className='cmd-history-sort'>
|
|
115
|
+
<Switch
|
|
116
|
+
checked={sortByFrequency}
|
|
117
|
+
onChange={handleSortByFrequencyChange}
|
|
118
|
+
size='small'
|
|
119
|
+
/>
|
|
120
|
+
<span
|
|
121
|
+
className='cmd-history-sort-label pointer'
|
|
122
|
+
onClick={() => handleSortByFrequencyChange(!sortByFrequency)}
|
|
123
|
+
>
|
|
124
|
+
{e('sortByFrequency')}
|
|
125
|
+
</span>
|
|
126
|
+
</div>
|
|
121
127
|
<UnorderedListOutlined
|
|
122
128
|
className='cmd-history-clear-icon pointer clear-ai-icon icon-hover'
|
|
123
129
|
title={e('clear')}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
Select,
|
|
4
4
|
Dropdown
|
|
5
5
|
} from 'antd'
|
|
6
|
-
import { InfoCircleOutlined, TranslationOutlined } from '@ant-design/icons'
|
|
6
|
+
import { InfoCircleOutlined, TranslationOutlined, DoubleRightOutlined } from '@ant-design/icons'
|
|
7
7
|
import './footer.styl'
|
|
8
8
|
import { statusMap } from '../../common/constants'
|
|
9
9
|
import BatchInput from './batch-input'
|
|
@@ -164,12 +164,29 @@ export default auto(function FooterEntry (props) {
|
|
|
164
164
|
)
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
function handleShowSidebar () {
|
|
168
|
+
window.store.toggleLeftSideBar()
|
|
169
|
+
}
|
|
170
|
+
|
|
167
171
|
const {
|
|
168
|
-
|
|
172
|
+
leftSidePanelWidth,
|
|
173
|
+
leftSideBarWidth,
|
|
169
174
|
openedSideBar,
|
|
170
175
|
inActiveTerminal
|
|
171
176
|
} = props.store
|
|
172
|
-
const w =
|
|
177
|
+
const w = leftSideBarWidth + leftSidePanelWidth
|
|
178
|
+
// icon bar hidden: show a control on the left of the footer to bring the
|
|
179
|
+
// sidebar back
|
|
180
|
+
const showSidebarIcon = leftSideBarWidth === 0
|
|
181
|
+
? (
|
|
182
|
+
<div className='terminal-footer-unit terminal-footer-show-sidebar'>
|
|
183
|
+
<DoubleRightOutlined
|
|
184
|
+
className='pointer font18 show-sidebar-icon'
|
|
185
|
+
onClick={handleShowSidebar}
|
|
186
|
+
/>
|
|
187
|
+
</div>
|
|
188
|
+
)
|
|
189
|
+
: null
|
|
173
190
|
const sideProps = openedSideBar
|
|
174
191
|
? {
|
|
175
192
|
className: 'main-footer',
|
|
@@ -184,12 +201,15 @@ export default auto(function FooterEntry (props) {
|
|
|
184
201
|
!inActiveTerminal
|
|
185
202
|
) {
|
|
186
203
|
return (
|
|
187
|
-
<div className='main-footer' {...sideProps}
|
|
204
|
+
<div className='main-footer' {...sideProps}>
|
|
205
|
+
{showSidebarIcon}
|
|
206
|
+
</div>
|
|
188
207
|
)
|
|
189
208
|
}
|
|
190
209
|
return (
|
|
191
210
|
<div {...sideProps}>
|
|
192
211
|
<div className='terminal-footer-flex'>
|
|
212
|
+
{showSidebarIcon}
|
|
193
213
|
{!isAIDisabled() && renderAIIcon()}
|
|
194
214
|
{renderCmdHistory()}
|
|
195
215
|
{renderQuickCommands()}
|
|
@@ -33,7 +33,8 @@ export default auto(function Layout (props) {
|
|
|
33
33
|
height,
|
|
34
34
|
pinnedQuickCommandBar,
|
|
35
35
|
// tabsHeight,
|
|
36
|
-
|
|
36
|
+
leftSidePanelWidth,
|
|
37
|
+
leftSideBarWidth,
|
|
37
38
|
// infoPanelPinned,
|
|
38
39
|
pinned,
|
|
39
40
|
rightPanelVisible,
|
|
@@ -43,7 +44,7 @@ export default auto(function Layout (props) {
|
|
|
43
44
|
inActiveTerminal
|
|
44
45
|
} = props.store
|
|
45
46
|
const h = height - footerHeight - (inActiveTerminal && pinnedQuickCommandBar ? quickCommandBoxHeight : 0) + resizeTrigger
|
|
46
|
-
const l = pinned ?
|
|
47
|
+
const l = pinned ? leftSideBarWidth + leftSidePanelWidth : leftSideBarWidth
|
|
47
48
|
const r = rightPanelVisible && rightPanelPinned ? rightPanelWidth : 0
|
|
48
49
|
return {
|
|
49
50
|
height: h,
|
|
@@ -59,15 +60,18 @@ export default auto(function Layout (props) {
|
|
|
59
60
|
height,
|
|
60
61
|
width,
|
|
61
62
|
pinnedQuickCommandBar,
|
|
62
|
-
|
|
63
|
+
leftSidePanelWidth,
|
|
64
|
+
leftSideBarWidth,
|
|
63
65
|
rightPanelVisible,
|
|
64
66
|
rightPanelPinned,
|
|
65
67
|
rightPanelWidth,
|
|
66
68
|
pinned
|
|
67
69
|
} = props.store
|
|
68
|
-
const l = pinned ?
|
|
70
|
+
const l = pinned ? leftSidePanelWidth : 0
|
|
69
71
|
const r = rightPanelPinned && rightPanelVisible ? rightPanelWidth : 0
|
|
70
|
-
|
|
72
|
+
// account for the far-left icon bar (sidebarWidth - 1px border on desktop;
|
|
73
|
+
// 0 when the bar is hidden on mobile)
|
|
74
|
+
const w = width - l - r - (leftSideBarWidth > 0 ? leftSideBarWidth - 1 : 0)
|
|
71
75
|
const h = height - footerHeight - (pinnedQuickCommandBar ? quickCommandBoxHeight : 0)
|
|
72
76
|
return layoutAlg(layout, w, h)
|
|
73
77
|
}
|
|
@@ -115,7 +119,7 @@ export default auto(function Layout (props) {
|
|
|
115
119
|
'pinnedQuickCommandBar',
|
|
116
120
|
'tabsHeight',
|
|
117
121
|
'appPath',
|
|
118
|
-
'
|
|
122
|
+
'leftSidePanelWidth',
|
|
119
123
|
'addPanelWidth',
|
|
120
124
|
'pinned',
|
|
121
125
|
'openedSideBar',
|
|
@@ -153,7 +157,8 @@ export default auto(function Layout (props) {
|
|
|
153
157
|
'pinnedQuickCommandBar',
|
|
154
158
|
'qmSortByFrequency',
|
|
155
159
|
'inActiveTerminal',
|
|
156
|
-
'
|
|
160
|
+
'leftSidePanelWidth',
|
|
161
|
+
'leftSideBarWidth',
|
|
157
162
|
'openedSideBar',
|
|
158
163
|
'currentQuickCommands'
|
|
159
164
|
])
|
|
@@ -177,7 +182,7 @@ export default auto(function Layout (props) {
|
|
|
177
182
|
'pinnedQuickCommandBar',
|
|
178
183
|
'tabsHeight',
|
|
179
184
|
'appPath',
|
|
180
|
-
'
|
|
185
|
+
'leftSidePanelWidth',
|
|
181
186
|
'pinned',
|
|
182
187
|
'openedSideBar',
|
|
183
188
|
'config',
|
|
@@ -119,7 +119,10 @@ export default auto(function Index (props) {
|
|
|
119
119
|
'is-desktop': !store.isMobile
|
|
120
120
|
})
|
|
121
121
|
const ext1 = {
|
|
122
|
-
className: cls
|
|
122
|
+
className: cls,
|
|
123
|
+
style: {
|
|
124
|
+
'--left-side-bar-width': store.leftSideBarWidth + 'px'
|
|
125
|
+
}
|
|
123
126
|
}
|
|
124
127
|
// Get active tab IDs
|
|
125
128
|
const activeTabIds = [
|
|
@@ -168,7 +171,8 @@ export default auto(function Index (props) {
|
|
|
168
171
|
'settingTab',
|
|
169
172
|
'settingItem',
|
|
170
173
|
'isSyncingSetting',
|
|
171
|
-
'
|
|
174
|
+
'leftSidePanelWidth',
|
|
175
|
+
'leftSideBarWidth',
|
|
172
176
|
'transferTab',
|
|
173
177
|
'sidebarPanelTab',
|
|
174
178
|
'openWidgetsModal'
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* quick commands footer selection wrap
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { useState
|
|
5
|
+
import { useState } from 'react'
|
|
6
6
|
import { quickCommandLabelsLsKey, pinnedQuickCommandBarKey } from '../../common/constants'
|
|
7
7
|
import { sortBy } from 'lodash-es'
|
|
8
8
|
import { Button, Input, Select, Space, Flex } from 'antd'
|
|
@@ -25,21 +25,6 @@ const { Option } = Select
|
|
|
25
25
|
export default function QuickCommandsFooterBox (props) {
|
|
26
26
|
const [keyword, setKeyword] = useState('')
|
|
27
27
|
const [label, setLabel] = useState(ls.getItem(quickCommandLabelsLsKey, ''))
|
|
28
|
-
const timer = useRef(null)
|
|
29
|
-
|
|
30
|
-
function handleMouseLeave () {
|
|
31
|
-
timer.current = setTimeout(() => {
|
|
32
|
-
toggle(false)
|
|
33
|
-
}, 500)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function handleMouseEnter () {
|
|
37
|
-
clearTimeout(timer.current)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function toggle (openQuickCommandBar) {
|
|
41
|
-
window.store.openQuickCommandBar = openQuickCommandBar
|
|
42
|
-
}
|
|
43
28
|
|
|
44
29
|
function handleTogglePinned () {
|
|
45
30
|
const current = !window.store.pinnedQuickCommandBar
|
|
@@ -176,7 +161,8 @@ export default function QuickCommandsFooterBox (props) {
|
|
|
176
161
|
pinnedQuickCommandBar,
|
|
177
162
|
qmSortByFrequency,
|
|
178
163
|
inActiveTerminal,
|
|
179
|
-
|
|
164
|
+
leftSidePanelWidth,
|
|
165
|
+
leftSideBarWidth,
|
|
180
166
|
openedSideBar
|
|
181
167
|
} = props
|
|
182
168
|
if ((!openQuickCommandBar && !pinnedQuickCommandBar) || !inActiveTerminal) {
|
|
@@ -203,14 +189,12 @@ export default function QuickCommandsFooterBox (props) {
|
|
|
203
189
|
: 'text'
|
|
204
190
|
const cls = classNames('qm-list-wrap')
|
|
205
191
|
const type = qmSortByFrequency ? 'primary' : 'default'
|
|
206
|
-
const w = openedSideBar ?
|
|
192
|
+
const w = openedSideBar ? leftSideBarWidth + leftSidePanelWidth : leftSideBarWidth
|
|
207
193
|
const qmProps = {
|
|
208
194
|
className: 'qm-wrap-tooltip',
|
|
209
195
|
style: {
|
|
210
196
|
left: w
|
|
211
|
-
}
|
|
212
|
-
onMouseLeave: handleMouseLeave,
|
|
213
|
-
onMouseEnter: handleMouseEnter
|
|
197
|
+
}
|
|
214
198
|
}
|
|
215
199
|
return (
|
|
216
200
|
<div
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* quick commands footer selection
|
|
3
|
+
*
|
|
4
|
+
* Click to toggle the quick-command bar. Click-based so it works identically
|
|
5
|
+
* on touch (mobile) and mouse (desktop) — the old hover-to-open model was
|
|
6
|
+
* unreliable on Android, where a tap synthesizes mouseleave and cancels the
|
|
7
|
+
* open timer.
|
|
3
8
|
*/
|
|
4
9
|
|
|
5
10
|
import { PureComponent } from 'react'
|
|
@@ -7,33 +12,18 @@ import { Button } from 'antd'
|
|
|
7
12
|
import './qm.styl'
|
|
8
13
|
|
|
9
14
|
export default class QuickCommandsFooter extends PureComponent {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
handleOpen = () => {
|
|
16
|
-
this.timer = setTimeout(this.act, 500)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
act = () => {
|
|
20
|
-
window.store.openQuickCommandBar = true
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
handleMouseLeave = () => {
|
|
24
|
-
clearTimeout(this.timer)
|
|
15
|
+
handleClick = () => {
|
|
16
|
+
const { store } = window
|
|
17
|
+
store.openQuickCommandBar = !store.openQuickCommandBar
|
|
25
18
|
}
|
|
26
19
|
|
|
27
20
|
render () {
|
|
28
21
|
return (
|
|
29
|
-
<div
|
|
30
|
-
className='fleft relative quick-command-trigger-wrap'
|
|
31
|
-
onMouseEnter={this.handleOpen}
|
|
32
|
-
onMouseLeave={this.handleMouseLeave}
|
|
33
|
-
>
|
|
22
|
+
<div className='fleft relative quick-command-trigger-wrap'>
|
|
34
23
|
<Button
|
|
35
24
|
size='small'
|
|
36
25
|
type='text'
|
|
26
|
+
onClick={this.handleClick}
|
|
37
27
|
>
|
|
38
28
|
Q
|
|
39
29
|
</Button>
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import { Component } from 'react'
|
|
6
6
|
import Drawer from '../common/drawer'
|
|
7
7
|
import { CloseCircleOutlined } from '@ant-design/icons'
|
|
8
|
-
import { sidebarWidth } from '../../common/constants'
|
|
9
8
|
import AppDrag from '../tabs/app-drag'
|
|
10
9
|
import './setting-wrap.styl'
|
|
11
10
|
|
|
@@ -33,7 +32,7 @@ export default class SettingWrap extends Component {
|
|
|
33
32
|
open: this.props.visible,
|
|
34
33
|
onClose: this.props.onCancel,
|
|
35
34
|
className: 'setting-wrap',
|
|
36
|
-
size: this.props.innerWidth -
|
|
35
|
+
size: this.props.innerWidth - window.store.leftSideBarWidth,
|
|
37
36
|
zIndex: 888,
|
|
38
37
|
placement: 'left'
|
|
39
38
|
}
|
|
@@ -10,7 +10,7 @@ export default auto(function BookmarkSelect (props) {
|
|
|
10
10
|
const {
|
|
11
11
|
listStyle,
|
|
12
12
|
openedSideBar,
|
|
13
|
-
|
|
13
|
+
leftSidePanelWidth,
|
|
14
14
|
expandedKeys,
|
|
15
15
|
bookmarks,
|
|
16
16
|
bookmarksMap,
|
|
@@ -38,7 +38,7 @@ export default auto(function BookmarkSelect (props) {
|
|
|
38
38
|
bookmarksMap,
|
|
39
39
|
bookmarkGroups: store.getBookmarkGroupsTotal(),
|
|
40
40
|
expandedKeys,
|
|
41
|
-
|
|
41
|
+
leftSidePanelWidth,
|
|
42
42
|
bookmarkGroupTree: store.bookmarkGroupTree,
|
|
43
43
|
autoFocus,
|
|
44
44
|
initLoadingData
|
|
@@ -44,22 +44,25 @@ export default auto(function HistoryPanel (props) {
|
|
|
44
44
|
}
|
|
45
45
|
return (
|
|
46
46
|
<div className='history-header pd2x pd2b'>
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
<div className='history-sort'>
|
|
48
|
+
<Switch
|
|
49
|
+
checked={sortByFrequency}
|
|
50
|
+
onChange={handleSortByFrequencyChange}
|
|
51
|
+
size='small'
|
|
52
|
+
/>
|
|
53
|
+
<span
|
|
54
|
+
className='history-sort-label pointer'
|
|
55
|
+
onClick={() => handleSortByFrequencyChange(!sortByFrequency)}
|
|
56
|
+
>
|
|
57
|
+
{e('sortByFrequency')}
|
|
58
|
+
</span>
|
|
59
|
+
</div>
|
|
50
60
|
<UnorderedListOutlined
|
|
51
61
|
{...clearIconProps}
|
|
52
62
|
/>
|
|
53
63
|
</div>
|
|
54
64
|
)
|
|
55
65
|
}
|
|
56
|
-
const switchProps = {
|
|
57
|
-
checkedChildren: e('sortByFrequency'),
|
|
58
|
-
unCheckedChildren: e('sortByFrequency'),
|
|
59
|
-
checked: sortByFrequency,
|
|
60
|
-
onChange: handleSortByFrequencyChange,
|
|
61
|
-
size: 'small'
|
|
62
|
-
}
|
|
63
66
|
const clearIconProps = {
|
|
64
67
|
className: 'history-clear-icon pointer clear-ai-icon icon-hover',
|
|
65
68
|
title: window.translate('clear'),
|
|
@@ -8,7 +8,8 @@ import {
|
|
|
8
8
|
UpCircleOutlined,
|
|
9
9
|
AppstoreOutlined,
|
|
10
10
|
ThunderboltOutlined,
|
|
11
|
-
AimOutlined
|
|
11
|
+
AimOutlined,
|
|
12
|
+
MenuFoldOutlined
|
|
12
13
|
} from '@ant-design/icons'
|
|
13
14
|
import { Tooltip, Popover } from 'antd'
|
|
14
15
|
import SideBarPanel from './sidebar-panel'
|
|
@@ -16,7 +17,6 @@ import TransferList from './transfer-list'
|
|
|
16
17
|
import MenuBtn from '../sys-menu/menu-btn'
|
|
17
18
|
import QuickConnect from '../tabs/quick-connect'
|
|
18
19
|
import {
|
|
19
|
-
sidebarWidth,
|
|
20
20
|
settingMap,
|
|
21
21
|
modals
|
|
22
22
|
} from '../../common/constants'
|
|
@@ -34,7 +34,10 @@ export default function Sidebar (props) {
|
|
|
34
34
|
settingTab,
|
|
35
35
|
settingItem,
|
|
36
36
|
isSyncingSetting,
|
|
37
|
-
|
|
37
|
+
// expandable bookmarks/history panel
|
|
38
|
+
leftSidePanelWidth,
|
|
39
|
+
// far-left icon bar (43px; 0 when hidden)
|
|
40
|
+
leftSideBarWidth,
|
|
38
41
|
pinned,
|
|
39
42
|
fileTransfers,
|
|
40
43
|
openedSideBar,
|
|
@@ -91,6 +94,10 @@ export default function Sidebar (props) {
|
|
|
91
94
|
store.onZoomReset()
|
|
92
95
|
}
|
|
93
96
|
|
|
97
|
+
const handleToggleSidebar = () => {
|
|
98
|
+
store.toggleLeftSideBar()
|
|
99
|
+
}
|
|
100
|
+
|
|
94
101
|
const {
|
|
95
102
|
onNewSsh,
|
|
96
103
|
openSetting,
|
|
@@ -115,16 +122,16 @@ export default function Sidebar (props) {
|
|
|
115
122
|
? {
|
|
116
123
|
className: 'sidebar-list',
|
|
117
124
|
style: {
|
|
118
|
-
width: `${
|
|
125
|
+
width: `${leftSidePanelWidth}px`
|
|
119
126
|
}
|
|
120
127
|
}
|
|
121
128
|
: {
|
|
122
129
|
className: 'sidebar-list'
|
|
123
130
|
}
|
|
124
131
|
const sidebarProps = {
|
|
125
|
-
className: `sidebar type-${openedSideBar}`,
|
|
132
|
+
className: `sidebar type-${openedSideBar}${leftSideBarWidth === 0 ? ' collapsed' : ''}`,
|
|
126
133
|
style: {
|
|
127
|
-
width:
|
|
134
|
+
width: leftSideBarWidth,
|
|
128
135
|
height
|
|
129
136
|
}
|
|
130
137
|
}
|
|
@@ -209,6 +216,14 @@ export default function Sidebar (props) {
|
|
|
209
216
|
onClick={openAbout}
|
|
210
217
|
/>
|
|
211
218
|
</SideIcon>
|
|
219
|
+
<SideIcon
|
|
220
|
+
title={e('hide')}
|
|
221
|
+
>
|
|
222
|
+
<MenuFoldOutlined
|
|
223
|
+
className='iblock font16 control-icon hide-sidebar-icon'
|
|
224
|
+
onClick={handleToggleSidebar}
|
|
225
|
+
/>
|
|
226
|
+
</SideIcon>
|
|
212
227
|
{
|
|
213
228
|
Math.round((zoom ?? 1) * 100) !== 100
|
|
214
229
|
? (
|
|
@@ -246,7 +261,8 @@ export default function Sidebar (props) {
|
|
|
246
261
|
<SidePanel
|
|
247
262
|
sideProps={sideProps}
|
|
248
263
|
setLeftSidePanelWidth={setLeftSidePanelWidth}
|
|
249
|
-
|
|
264
|
+
leftSidePanelWidth={leftSidePanelWidth}
|
|
265
|
+
leftSideBarWidth={leftSideBarWidth}
|
|
250
266
|
>
|
|
251
267
|
<SideBarPanel
|
|
252
268
|
pinned={pinned}
|
|
@@ -15,13 +15,13 @@ export default function SidePanel (props) {
|
|
|
15
15
|
}
|
|
16
16
|
const el1 = document.querySelector('.sessions')
|
|
17
17
|
if (el1) {
|
|
18
|
-
el1.style.left = (nw +
|
|
18
|
+
el1.style.left = (nw + props.leftSideBarWidth) + 'px'
|
|
19
19
|
}
|
|
20
|
-
}, [props.
|
|
20
|
+
}, [props.leftSidePanelWidth, props.leftSideBarWidth])
|
|
21
21
|
const dragProps = {
|
|
22
22
|
min: 343,
|
|
23
23
|
max: 600,
|
|
24
|
-
width: props.
|
|
24
|
+
width: props.leftSidePanelWidth,
|
|
25
25
|
onDragEnd,
|
|
26
26
|
onDragMove,
|
|
27
27
|
left: true
|
|
@@ -57,6 +57,12 @@
|
|
|
57
57
|
top 0
|
|
58
58
|
z-index 10
|
|
59
59
|
background var(--main)
|
|
60
|
+
.history-sort
|
|
61
|
+
display flex
|
|
62
|
+
align-items center
|
|
63
|
+
gap 6px
|
|
64
|
+
.history-sort-label
|
|
65
|
+
user-select none
|
|
60
66
|
.history-clear-icon
|
|
61
67
|
margin-left auto
|
|
62
68
|
margin-right 0
|
|
@@ -93,12 +99,17 @@
|
|
|
93
99
|
color var(--text-light)
|
|
94
100
|
.sidebar-list
|
|
95
101
|
position absolute
|
|
96
|
-
left 43px
|
|
102
|
+
left var(--left-side-bar-width, 43px)
|
|
97
103
|
top 0
|
|
98
104
|
bottom 0
|
|
99
105
|
z-index 200
|
|
100
106
|
width 0
|
|
101
107
|
|
|
108
|
+
// mobile: icon bar hidden — hide its buttons
|
|
109
|
+
.sidebar.collapsed
|
|
110
|
+
.sidebar-bar
|
|
111
|
+
display none
|
|
112
|
+
|
|
102
113
|
.pinned
|
|
103
114
|
.sidebar-list
|
|
104
115
|
top 0
|
|
@@ -51,7 +51,7 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
|
|
|
51
51
|
)}
|
|
52
52
|
<QuickConnect batch={batch} />
|
|
53
53
|
</div>
|
|
54
|
-
<div className='
|
|
54
|
+
<div className='no-session-logo'>
|
|
55
55
|
<LogoElem />
|
|
56
56
|
</div>
|
|
57
57
|
<div className='no-session-history' onClick={handleClick}>
|
|
@@ -1,28 +1,51 @@
|
|
|
1
1
|
.no-sessions
|
|
2
2
|
background var(--main)
|
|
3
3
|
text-align center
|
|
4
|
-
|
|
4
|
+
// stack buttons / logo / history; the history flexes to fill the remaining
|
|
5
|
+
// height, so the layout adapts to logo size and button wrapping without any
|
|
6
|
+
// fixed pixel offsets.
|
|
7
|
+
display flex
|
|
8
|
+
flex-direction column
|
|
5
9
|
position absolute
|
|
6
10
|
top 36px
|
|
7
11
|
left 0
|
|
8
12
|
right 0
|
|
9
13
|
bottom 0
|
|
14
|
+
padding 50px 0
|
|
10
15
|
z-index 11
|
|
11
16
|
.no-session-btns
|
|
17
|
+
flex-shrink 0
|
|
12
18
|
display flex
|
|
13
19
|
flex-wrap wrap
|
|
14
20
|
justify-content center
|
|
15
21
|
align-items center
|
|
16
22
|
gap 8px
|
|
17
23
|
padding-bottom 8px
|
|
24
|
+
.no-session-logo
|
|
25
|
+
flex-shrink 0
|
|
26
|
+
padding 32px 0
|
|
18
27
|
.no-session-history
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
flex 1
|
|
29
|
+
min-height 0
|
|
30
|
+
display flex
|
|
31
|
+
flex-direction column
|
|
32
|
+
overflow hidden
|
|
24
33
|
.sidebar-panel-history
|
|
25
|
-
|
|
34
|
+
// override the global sidebar absolute positioning (top:112px) so the
|
|
35
|
+
// history fills this flex item instead.
|
|
36
|
+
position static
|
|
37
|
+
flex 1
|
|
38
|
+
min-height 0
|
|
26
39
|
.pd2x
|
|
27
40
|
width 400px
|
|
28
|
-
margin 0 auto
|
|
41
|
+
margin 0 auto
|
|
42
|
+
.is-mobile
|
|
43
|
+
.no-sessions
|
|
44
|
+
padding 20px 0
|
|
45
|
+
.no-session-logo
|
|
46
|
+
padding 8px 0
|
|
47
|
+
.no-session-history
|
|
48
|
+
.sidebar-panel-history
|
|
49
|
+
.pd2x
|
|
50
|
+
width auto
|
|
51
|
+
max-width 400px
|
|
@@ -147,9 +147,17 @@ export async function detectRemoteShell (pid) {
|
|
|
147
147
|
// instead of probing for any shell binary installed on the host.
|
|
148
148
|
const cmd = 'printf "%s\n" "$SHELL"'
|
|
149
149
|
|
|
150
|
-
|
|
150
|
+
// { silent: true } so this best-effort probe does not emit a transport-level
|
|
151
|
+
// fetch warning; a single, clearer warning is logged below if it fails.
|
|
152
|
+
const r = await runCmd(pid, cmd, { silent: true })
|
|
151
153
|
.catch((err) => {
|
|
152
|
-
|
|
154
|
+
// Non-fatal: the interactive shell already opened, so the terminal keeps
|
|
155
|
+
// working. We just can't inject OSC 633 shell integration for command
|
|
156
|
+
// tracking. This commonly happens when the server limits concurrent
|
|
157
|
+
// sessions (sshd MaxSessions) or the account uses a forced/restricted
|
|
158
|
+
// command, which rejects the auxiliary exec channel with
|
|
159
|
+
// "(SSH) Channel open failure: open failed".
|
|
160
|
+
console.warn('detectRemoteShell: exec channel rejected by server, shell integration disabled, falling back to sh —', err?.message || err)
|
|
153
161
|
return 'sh'
|
|
154
162
|
})
|
|
155
163
|
|
|
@@ -11,21 +11,21 @@ export function createTerm (body) {
|
|
|
11
11
|
})
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export function runCmd (pid, cmd) {
|
|
14
|
+
export function runCmd (pid, cmd, options) {
|
|
15
15
|
return fetch({
|
|
16
16
|
pid,
|
|
17
17
|
cmd,
|
|
18
18
|
action: 'run-cmd'
|
|
19
|
-
})
|
|
19
|
+
}, options)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function execCmd (pid, cmd, timeoutMs) {
|
|
22
|
+
export function execCmd (pid, cmd, timeoutMs, options) {
|
|
23
23
|
return fetch({
|
|
24
24
|
pid,
|
|
25
25
|
cmd,
|
|
26
26
|
timeoutMs,
|
|
27
27
|
action: 'exec-cmd'
|
|
28
|
-
})
|
|
28
|
+
}, options)
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
export function resizeTerm (pid, cols, rows) {
|
|
@@ -166,13 +166,21 @@ class Term extends Component {
|
|
|
166
166
|
!isEqual(
|
|
167
167
|
pick(this.props, names),
|
|
168
168
|
pick(prevProps, names)
|
|
169
|
-
)
|
|
170
|
-
shouldChange
|
|
169
|
+
)
|
|
171
170
|
) {
|
|
172
171
|
this.onResize()
|
|
173
172
|
}
|
|
174
173
|
if (shouldChange && this.term) {
|
|
175
174
|
this.term.focus()
|
|
175
|
+
// 标签从 display:none 变为可见时,延迟到下一动画帧再 fit,读取正确布局
|
|
176
|
+
// 尺寸纠正列数;并用 refresh 强制重绘,清除快速连续打开多个连接时隐藏
|
|
177
|
+
// 标签被 0 列 fit 导致的虚假/重复提示符。
|
|
178
|
+
requestAnimationFrame(() => {
|
|
179
|
+
if (this.term && !this.onClose) {
|
|
180
|
+
this.fitAndRefresh()
|
|
181
|
+
setTimeout(() => this.fitAndRefresh(), 80)
|
|
182
|
+
}
|
|
183
|
+
})
|
|
176
184
|
}
|
|
177
185
|
this.checkConfigChange(
|
|
178
186
|
prevProps,
|
|
@@ -1530,7 +1538,11 @@ class Term extends Component {
|
|
|
1530
1538
|
this.term = term
|
|
1531
1539
|
term.onSelectionChange(this.onSelectionChange)
|
|
1532
1540
|
term.attachCustomKeyEventHandler(this.handleKeyboardEvent.bind(this))
|
|
1533
|
-
|
|
1541
|
+
// 容器不可见(隐藏标签 display:none)时不 fit,避免算出 0 列把 shell
|
|
1542
|
+
// 提示符逐字符错误换行;待标签激活可见后由 fitAndRefresh 重新适配。
|
|
1543
|
+
if (this.isElementVisible()) {
|
|
1544
|
+
this.fitAddon.fit()
|
|
1545
|
+
}
|
|
1534
1546
|
await this.remoteInit(term)
|
|
1535
1547
|
}
|
|
1536
1548
|
|
|
@@ -1888,7 +1900,10 @@ class Term extends Component {
|
|
|
1888
1900
|
this.trzszClient.init(socket)
|
|
1889
1901
|
this.xmodemClient = new XmodemClient(this)
|
|
1890
1902
|
this.xmodemClient.init(socket)
|
|
1891
|
-
|
|
1903
|
+
// 仅在可见时 fit,隐藏标签跳过,避免 0 列损坏提示符。
|
|
1904
|
+
if (this.isElementVisible()) {
|
|
1905
|
+
this.fitAddon.fit()
|
|
1906
|
+
}
|
|
1892
1907
|
term.displayRaw = displayRaw
|
|
1893
1908
|
term.loadAddon(
|
|
1894
1909
|
new KeywordHighlighterAddon(keywords)
|
|
@@ -1952,20 +1967,38 @@ class Term extends Component {
|
|
|
1952
1967
|
})
|
|
1953
1968
|
}
|
|
1954
1969
|
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
this.term
|
|
1962
|
-
) {
|
|
1963
|
-
try {
|
|
1964
|
-
this.fitAddon.fit()
|
|
1965
|
-
} catch (e) {
|
|
1966
|
-
console.info('resize failed')
|
|
1967
|
-
}
|
|
1970
|
+
// 判断终端挂载容器当前是否真实可见(非 display:none)。
|
|
1971
|
+
// 隐藏标签的 clientWidth/clientHeight 为 0,此时不应 fit,否则会算出 0 列。
|
|
1972
|
+
isElementVisible = () => {
|
|
1973
|
+
const el = this.domRef.current
|
|
1974
|
+
if (!el) {
|
|
1975
|
+
return false
|
|
1968
1976
|
}
|
|
1977
|
+
return el.clientWidth > 0 && el.clientHeight > 0
|
|
1978
|
+
}
|
|
1979
|
+
|
|
1980
|
+
// 重新适配终端尺寸并强制重绘可见区域。
|
|
1981
|
+
// 仅当容器真正可见时才 fit:隐藏标签(display:none)下 clientWidth 为 0,
|
|
1982
|
+
// 若在此时 fit 会算出 0/极小列数,使 shell 提示符被逐字符错误换行,
|
|
1983
|
+
// 出现快速连续打开多个连接时的虚假/重复提示符。因此隐藏态跳过 fit,
|
|
1984
|
+
// 保留 xterm 默认 80x24,待标签激活可见后再 fit 重绘,从根上避免换行损坏。
|
|
1985
|
+
fitAndRefresh = () => {
|
|
1986
|
+
if (!this.term || !this.fitAddon || this.onClose) {
|
|
1987
|
+
return
|
|
1988
|
+
}
|
|
1989
|
+
if (!this.isElementVisible()) {
|
|
1990
|
+
return
|
|
1991
|
+
}
|
|
1992
|
+
try {
|
|
1993
|
+
this.fitAddon.fit()
|
|
1994
|
+
this.term.refresh(0, this.term.rows - 1)
|
|
1995
|
+
} catch (e) {
|
|
1996
|
+
console.info('resize failed', e)
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
|
|
2000
|
+
onResize = throttle(() => {
|
|
2001
|
+
this.fitAndRefresh()
|
|
1969
2002
|
}, 200)
|
|
1970
2003
|
|
|
1971
2004
|
onerrorSocket = err => {
|
|
@@ -12,7 +12,7 @@ export default function TreeListRow (props) {
|
|
|
12
12
|
activeItemId,
|
|
13
13
|
searchSelectedRowKey,
|
|
14
14
|
staticList,
|
|
15
|
-
|
|
15
|
+
leftSidePanelWidth,
|
|
16
16
|
handleExpand,
|
|
17
17
|
handleUnExpand,
|
|
18
18
|
del,
|
|
@@ -43,7 +43,7 @@ export default function TreeListRow (props) {
|
|
|
43
43
|
itemColor: item?.color,
|
|
44
44
|
itemDescription: item?.description,
|
|
45
45
|
itemLevel: item?.level,
|
|
46
|
-
|
|
46
|
+
leftSidePanelWidth,
|
|
47
47
|
staticList,
|
|
48
48
|
selectedItemId: activeItemId,
|
|
49
49
|
searchSelected: searchSelectedRowKey === row.key,
|
|
@@ -748,7 +748,7 @@ export default class ItemListTree extends Component {
|
|
|
748
748
|
activeItemId={this.props.activeItemId}
|
|
749
749
|
searchSelectedRowKey={this.state.searchSelectedRowKey}
|
|
750
750
|
staticList={this.props.staticList}
|
|
751
|
-
|
|
751
|
+
leftSidePanelWidth={this.props.leftSidePanelWidth}
|
|
752
752
|
{...pick(
|
|
753
753
|
this,
|
|
754
754
|
[
|
package/client/css/mobile.styl
CHANGED
|
@@ -93,7 +93,8 @@
|
|
|
93
93
|
// ---- 3. Footer encode select: show icon, click to open dropdown
|
|
94
94
|
// match antd Button small icon size (~14px) used by sibling icons
|
|
95
95
|
.mobile-encode-trigger,
|
|
96
|
-
.terminal-info-icon
|
|
96
|
+
.terminal-info-icon,
|
|
97
|
+
.show-sidebar-icon
|
|
97
98
|
font-size 14px !important
|
|
98
99
|
line-height 36px
|
|
99
100
|
padding 0 4px
|
|
@@ -177,12 +178,21 @@
|
|
|
177
178
|
|
|
178
179
|
// ---------------------------------------------------------------------------
|
|
179
180
|
// Touch-primary devices only (feature-detected via pointer media query, not
|
|
180
|
-
// UA sniffing or viewport width).
|
|
181
|
-
//
|
|
182
|
-
//
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
//
|
|
181
|
+
// UA sniffing or viewport width). Desktop browsers (even with touchscreens or
|
|
182
|
+
// narrow windows) report pointer: fine, so this never matches them.
|
|
183
|
+
//
|
|
184
|
+
// 1. iOS Safari and Android Chrome auto-zoom the page when focusing an input
|
|
185
|
+
// with font-size < 16px, and never zoom back out on blur, leaving the page
|
|
186
|
+
// stuck zoomed. Force 16px on all focusable form fields (antd inputs default
|
|
187
|
+
// to 14px) to prevent the zoom.
|
|
188
|
+
//
|
|
189
|
+
// 2. Mobile browsers also inflate text to improve readability
|
|
190
|
+
// (-webkit-text-size-adjust defaults to auto). Inside a fixed-size container
|
|
191
|
+
// this makes the text taller than its wrapper, so it gets clipped — the
|
|
192
|
+
// clearest victim is the antd small Switch, whose track is only 16px tall /
|
|
193
|
+
// 28px wide with overflow:hidden; the "sort by frequency" label gets blown
|
|
194
|
+
// past the track and chopped. Pin to 100% so the declared font-size is
|
|
195
|
+
// honored.
|
|
186
196
|
// ---------------------------------------------------------------------------
|
|
187
197
|
@media (pointer: coarse)
|
|
188
198
|
input,
|
|
@@ -193,3 +203,6 @@
|
|
|
193
203
|
.ant-input-number-input,
|
|
194
204
|
.ant-select-selection-search-input
|
|
195
205
|
font-size 16px !important
|
|
206
|
+
html
|
|
207
|
+
-webkit-text-size-adjust 100%
|
|
208
|
+
text-size-adjust 100%
|
package/client/store/common.js
CHANGED
|
@@ -7,7 +7,8 @@ import Modal from '../components/common/modal'
|
|
|
7
7
|
import { appendMandatoryGuardrails } from '../components/ai/ai-guardrails'
|
|
8
8
|
import { debounce, some, get, pickBy } from 'lodash-es'
|
|
9
9
|
import {
|
|
10
|
-
|
|
10
|
+
leftSidePanelWidthKey,
|
|
11
|
+
leftSideBarOpenKey,
|
|
11
12
|
rightSidebarWidthKey,
|
|
12
13
|
addPanelWidthLsKey,
|
|
13
14
|
dismissDelKeyTipLsKey,
|
|
@@ -106,8 +107,19 @@ export default Store => {
|
|
|
106
107
|
}
|
|
107
108
|
|
|
108
109
|
Store.prototype.setLeftSidePanelWidth = function (v) {
|
|
109
|
-
ls.setItem(
|
|
110
|
-
window.store.
|
|
110
|
+
ls.setItem(leftSidePanelWidthKey, v)
|
|
111
|
+
window.store._leftSidePanelWidth = v
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
Store.prototype.toggleLeftSideBar = function () {
|
|
115
|
+
const { store } = window
|
|
116
|
+
const willOpen = !store._leftSideBarOpen
|
|
117
|
+
store._leftSideBarOpen = willOpen
|
|
118
|
+
ls.setItem(leftSideBarOpenKey, willOpen ? 'true' : 'false')
|
|
119
|
+
if (!willOpen) {
|
|
120
|
+
// hiding the bar also closes/unpins the side panel so no space is reserved
|
|
121
|
+
store.handleCloseSidebar()
|
|
122
|
+
}
|
|
111
123
|
}
|
|
112
124
|
|
|
113
125
|
Store.prototype.setAddPanelWidth = function (v) {
|
|
@@ -9,13 +9,14 @@ import {
|
|
|
9
9
|
infoTabs,
|
|
10
10
|
openedSidebarKey,
|
|
11
11
|
sidebarPinnedKey,
|
|
12
|
+
leftSideBarOpenKey,
|
|
12
13
|
pinnedQuickCommandBarKey,
|
|
13
14
|
sftpDefaultSortSettingKey,
|
|
14
15
|
batchInputLsKey,
|
|
15
16
|
expandedKeysLsKey,
|
|
16
17
|
checkedKeysLsKey,
|
|
17
18
|
localAddrBookmarkLsKey,
|
|
18
|
-
|
|
19
|
+
leftSidePanelWidthKey,
|
|
19
20
|
rightSidebarWidthKey,
|
|
20
21
|
addPanelWidthLsKey,
|
|
21
22
|
dismissDelKeyTipLsKey,
|
|
@@ -46,6 +47,10 @@ function getDefaultBookmarkGroups (bookmarks) {
|
|
|
46
47
|
|
|
47
48
|
export default () => {
|
|
48
49
|
const layout = ls.getItem('layout') || splitMap.c1
|
|
50
|
+
// far-left icon bar is open by default; once the user toggles it, honor the
|
|
51
|
+
// saved preference ('true'/'false') from localStorage.
|
|
52
|
+
const storedSideBarOpen = ls.getItem(leftSideBarOpenKey)
|
|
53
|
+
const leftSideBarOpen = storedSideBarOpen === '' ? true : storedSideBarOpen === 'true'
|
|
49
54
|
return {
|
|
50
55
|
// common
|
|
51
56
|
wsInited: false,
|
|
@@ -164,7 +169,9 @@ export default () => {
|
|
|
164
169
|
|
|
165
170
|
// sidebar
|
|
166
171
|
openedSideBar: ls.getItem(openedSidebarKey) || '',
|
|
167
|
-
|
|
172
|
+
_leftSidePanelWidth: parseInt(ls.getItem(leftSidePanelWidthKey), 10) || 300,
|
|
173
|
+
// whether the far-left icon bar is open (hide/show works on every platform)
|
|
174
|
+
_leftSideBarOpen: leftSideBarOpen,
|
|
168
175
|
addPanelWidth: parseInt(ls.getItem(addPanelWidthLsKey), 10) || 300,
|
|
169
176
|
menuOpened: false,
|
|
170
177
|
pinned: ls.getItem(sidebarPinnedKey) === 'true',
|
package/client/store/store.js
CHANGED
|
@@ -59,12 +59,21 @@ class Store {
|
|
|
59
59
|
return window.store.innerWidth
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
get
|
|
62
|
+
get leftSidePanelWidth () {
|
|
63
63
|
const { store } = window
|
|
64
64
|
if (store.isMobile) {
|
|
65
|
-
return Math.min(store.innerWidth -
|
|
65
|
+
return Math.min(store.innerWidth - store.leftSideBarWidth, 500)
|
|
66
66
|
}
|
|
67
|
-
return store.
|
|
67
|
+
return store._leftSidePanelWidth
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// width of the far-left icon bar (the ~43px vertical strip with the menu
|
|
71
|
+
// / bookmarks / settings icons). Distinct from leftSidePanelWidth, which is
|
|
72
|
+
// the expandable bookmarks/history panel. Controlled by _leftSideBarOpen on
|
|
73
|
+
// every platform: sidebarWidth when open, 0 when hidden.
|
|
74
|
+
get leftSideBarWidth () {
|
|
75
|
+
const { store } = window
|
|
76
|
+
return store._leftSideBarOpen ? sidebarWidth : 0
|
|
68
77
|
}
|
|
69
78
|
|
|
70
79
|
get rightPanelWidth () {
|