@electerm/electerm-react 5.0.7 → 5.1.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/client/common/constants.js +2 -0
- package/client/common/default-setting.js +10 -1
- package/client/common/left-sidebar-icon-defs.js +36 -0
- package/client/components/ai/ai-config-props.js +8 -0
- package/client/components/bookmark-form/common/fields.jsx +3 -2
- package/client/components/bookmark-form/common/ssh-agent.jsx +3 -2
- package/client/components/bookmark-form/common/x11.jsx +2 -2
- package/client/components/common/logo-elem.jsx +1 -1
- package/client/components/common/logo.styl +8 -2
- package/client/components/common/switch.jsx +63 -0
- package/client/components/common/switch.styl +7 -0
- package/client/components/footer/cmd-history.jsx +10 -8
- package/client/components/footer/cmd-history.styl +8 -0
- package/client/components/layout/layout.jsx +8 -5
- package/client/components/main/main.jsx +7 -2
- package/client/components/profile/profile-form-elem.jsx +2 -2
- package/client/components/quick-commands/quick-commands-box.jsx +2 -19
- package/client/components/quick-commands/quick-commands-form-elem.jsx +2 -2
- package/client/components/quick-commands/quick-commands-select.jsx +10 -20
- package/client/components/rdp/rdp-session.jsx +3 -4
- package/client/components/setting-panel/setting-common.jsx +9 -5
- package/client/components/setting-panel/setting-left-sidebar-icons.jsx +54 -0
- package/client/components/setting-panel/setting-terminal.jsx +3 -4
- package/client/components/setting-sync/data-import.jsx +3 -4
- package/client/components/setting-sync/setting-sync-form.jsx +3 -2
- package/client/components/sftp/file-compare-modal.jsx +1 -1
- package/client/components/sidebar/history.jsx +9 -11
- package/client/components/sidebar/index.jsx +22 -71
- package/client/components/sidebar/left-sidebar-icons.jsx +148 -0
- package/client/components/sidebar/sidebar.styl +6 -0
- package/client/components/sidebar/transfer-list.jsx +1 -1
- package/client/components/spice/spice-session.jsx +4 -5
- package/client/components/tabs/no-session.jsx +1 -1
- package/client/components/tabs/no-session.styl +31 -8
- package/client/components/terminal/shortcut-bar-defs.js +131 -0
- package/client/components/terminal/shortcut-bar-edit.jsx +205 -0
- package/client/components/terminal/shortcut-bar-entry.jsx +34 -0
- package/client/components/terminal/shortcut-bar.jsx +150 -0
- package/client/components/terminal/shortcut-bar.styl +186 -0
- package/client/components/terminal/terminal-color-query.mjs +47 -0
- package/client/components/terminal/terminal.jsx +39 -1
- package/client/components/terminal/terminal.styl +8 -0
- package/client/components/terminal-info/base.jsx +5 -7
- package/client/components/widgets/widget-form.jsx +3 -2
- package/client/css/mobile.styl +18 -6
- package/client/store/init-state.js +3 -0
- package/client/store/mcp-handler.js +29 -1
- package/package.json +1 -1
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
Button,
|
|
7
|
-
Switch,
|
|
8
7
|
Select,
|
|
9
8
|
Space
|
|
10
9
|
} from 'antd'
|
|
@@ -13,6 +12,7 @@ import {
|
|
|
13
12
|
ExportOutlined
|
|
14
13
|
} from '@ant-design/icons'
|
|
15
14
|
import Upload from '../common/upload'
|
|
15
|
+
import SwitchLabel from '../common/switch'
|
|
16
16
|
import HelpIcon from '../common/help-icon'
|
|
17
17
|
|
|
18
18
|
const e = window.translate
|
|
@@ -87,11 +87,10 @@ export default function DataTransport (props) {
|
|
|
87
87
|
</Upload>
|
|
88
88
|
</div>
|
|
89
89
|
<div className='fright'>
|
|
90
|
-
<
|
|
90
|
+
<SwitchLabel
|
|
91
91
|
checked={autoSyncEnabled}
|
|
92
|
-
|
|
92
|
+
label={txt}
|
|
93
93
|
onChange={handleAutoSync}
|
|
94
|
-
unCheckedChildren={txt}
|
|
95
94
|
className='mg3l mg1r'
|
|
96
95
|
/>
|
|
97
96
|
{autoSyncEnabled && (
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { useEffect, useRef } from 'react'
|
|
9
9
|
import { ArrowDownOutlined, ArrowUpOutlined, SaveOutlined, ClearOutlined } from '@ant-design/icons'
|
|
10
|
-
import { Button, Input, Form, Alert
|
|
10
|
+
import { Button, Input, Form, Alert } from 'antd'
|
|
11
11
|
import { notification } from '../common/notification'
|
|
12
|
+
import SwitchLabel from '../common/switch'
|
|
12
13
|
import Link from '../common/external-link'
|
|
13
14
|
import dayjs from 'dayjs'
|
|
14
15
|
import eq from 'fast-deep-equal'
|
|
@@ -253,7 +254,7 @@ export default function SyncForm (props) {
|
|
|
253
254
|
name='skipVerify'
|
|
254
255
|
valuePropName='checked'
|
|
255
256
|
>
|
|
256
|
-
<
|
|
257
|
+
<SwitchLabel />
|
|
257
258
|
</FormItem>
|
|
258
259
|
<FormItem
|
|
259
260
|
label={createLabel(e('password'))}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import React, { useState, useEffect } from 'react'
|
|
6
6
|
import { auto } from 'manate/react'
|
|
7
|
-
import { Switch } from 'antd'
|
|
8
7
|
import { UnorderedListOutlined } from '@ant-design/icons'
|
|
8
|
+
import SwitchLabel from '../common/switch'
|
|
9
9
|
import HistoryItem from './history-item'
|
|
10
10
|
import { getItemJSON, setItemJSON } from '../../common/safe-local-storage.js'
|
|
11
11
|
import '../setting-panel/list.styl'
|
|
@@ -44,22 +44,20 @@ 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
|
+
<SwitchLabel
|
|
49
|
+
checked={sortByFrequency}
|
|
50
|
+
onChange={handleSortByFrequencyChange}
|
|
51
|
+
size='small'
|
|
52
|
+
label={e('sortByFrequency')}
|
|
53
|
+
/>
|
|
54
|
+
</div>
|
|
50
55
|
<UnorderedListOutlined
|
|
51
56
|
{...clearIconProps}
|
|
52
57
|
/>
|
|
53
58
|
</div>
|
|
54
59
|
)
|
|
55
60
|
}
|
|
56
|
-
const switchProps = {
|
|
57
|
-
checkedChildren: e('sortByFrequency'),
|
|
58
|
-
unCheckedChildren: e('sortByFrequency'),
|
|
59
|
-
checked: sortByFrequency,
|
|
60
|
-
onChange: handleSortByFrequencyChange,
|
|
61
|
-
size: 'small'
|
|
62
|
-
}
|
|
63
61
|
const clearIconProps = {
|
|
64
62
|
className: 'history-clear-icon pointer clear-ai-icon icon-hover',
|
|
65
63
|
title: window.translate('clear'),
|
|
@@ -1,27 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
-
BookOutlined,
|
|
3
|
-
CloudSyncOutlined,
|
|
4
2
|
InfoCircleOutlined,
|
|
5
|
-
PictureOutlined,
|
|
6
|
-
PlusCircleOutlined,
|
|
7
|
-
SettingOutlined,
|
|
8
3
|
UpCircleOutlined,
|
|
9
|
-
AppstoreOutlined,
|
|
10
|
-
ThunderboltOutlined,
|
|
11
4
|
AimOutlined,
|
|
12
5
|
MenuFoldOutlined
|
|
13
6
|
} from '@ant-design/icons'
|
|
14
|
-
import { Tooltip
|
|
7
|
+
import { Tooltip } from 'antd'
|
|
15
8
|
import SideBarPanel from './sidebar-panel'
|
|
16
|
-
import TransferList from './transfer-list'
|
|
17
9
|
import MenuBtn from '../sys-menu/menu-btn'
|
|
18
|
-
import QuickConnect from '../tabs/quick-connect'
|
|
19
10
|
import {
|
|
20
11
|
settingMap,
|
|
21
12
|
modals
|
|
22
13
|
} from '../../common/constants'
|
|
23
14
|
import SideIcon from './side-icon'
|
|
24
15
|
import SidePanel from './side-panel'
|
|
16
|
+
import LeftSidebarIcons from './left-sidebar-icons'
|
|
17
|
+
import TransferList from './transfer-list'
|
|
25
18
|
import hasActiveInput from '../../common/has-active-input'
|
|
26
19
|
import './sidebar.styl'
|
|
27
20
|
|
|
@@ -47,7 +40,9 @@ export default function Sidebar (props) {
|
|
|
47
40
|
showInfoModal,
|
|
48
41
|
sidebarPanelTab,
|
|
49
42
|
openWidgetsModal,
|
|
50
|
-
zoom
|
|
43
|
+
zoom,
|
|
44
|
+
leftSideBarIcons,
|
|
45
|
+
widgetInstancesLength
|
|
51
46
|
} = props
|
|
52
47
|
|
|
53
48
|
const { store } = window
|
|
@@ -146,67 +141,23 @@ export default function Sidebar (props) {
|
|
|
146
141
|
<div className='control-icon-wrap'>
|
|
147
142
|
<MenuBtn store={store} config={store.config} />
|
|
148
143
|
</div>
|
|
149
|
-
<
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
/>
|
|
166
|
-
</div>
|
|
167
|
-
</Popover>
|
|
168
|
-
<SideIcon
|
|
169
|
-
title={e(settingMap.bookmarks)}
|
|
170
|
-
active={bookmarksActive}
|
|
171
|
-
>
|
|
172
|
-
<BookOutlined
|
|
173
|
-
onClick={handleClickBookmark}
|
|
174
|
-
className='font20 iblock control-icon'
|
|
175
|
-
/>
|
|
176
|
-
</SideIcon>
|
|
144
|
+
<LeftSidebarIcons
|
|
145
|
+
iconIds={leftSideBarIcons}
|
|
146
|
+
handleClickBookmark={handleClickBookmark}
|
|
147
|
+
onNewSsh={onNewSsh}
|
|
148
|
+
openSetting={openSetting}
|
|
149
|
+
openSettingSync={openSettingSync}
|
|
150
|
+
openTerminalThemes={openTerminalThemes}
|
|
151
|
+
openWidgetsModal={openWidgetsModal}
|
|
152
|
+
bookmarksActive={bookmarksActive}
|
|
153
|
+
themeActive={themeActive}
|
|
154
|
+
settingActive={settingActive}
|
|
155
|
+
syncActive={syncActive}
|
|
156
|
+
widgetsActive={widgetsActive}
|
|
157
|
+
isSyncingSetting={isSyncingSetting}
|
|
158
|
+
widgetInstancesLength={widgetInstancesLength}
|
|
159
|
+
/>
|
|
177
160
|
<TransferList {...transferProps} />
|
|
178
|
-
<SideIcon
|
|
179
|
-
title={e(settingMap.terminalThemes)}
|
|
180
|
-
active={themeActive}
|
|
181
|
-
>
|
|
182
|
-
<PictureOutlined
|
|
183
|
-
className='font20 iblock pointer control-icon'
|
|
184
|
-
onClick={openTerminalThemes}
|
|
185
|
-
/>
|
|
186
|
-
</SideIcon>
|
|
187
|
-
<SideIcon
|
|
188
|
-
title={e(settingMap.setting)}
|
|
189
|
-
active={settingActive}
|
|
190
|
-
>
|
|
191
|
-
<SettingOutlined className='iblock font20 control-icon' onClick={openSetting} />
|
|
192
|
-
</SideIcon>
|
|
193
|
-
<SideIcon
|
|
194
|
-
title={e('settingSync')}
|
|
195
|
-
active={syncActive}
|
|
196
|
-
>
|
|
197
|
-
<CloudSyncOutlined
|
|
198
|
-
className='iblock font20 control-icon'
|
|
199
|
-
onClick={openSettingSync}
|
|
200
|
-
spin={isSyncingSetting}
|
|
201
|
-
/>
|
|
202
|
-
</SideIcon>
|
|
203
|
-
<SideIcon
|
|
204
|
-
title={e('widgets')}
|
|
205
|
-
active={widgetsActive}
|
|
206
|
-
>
|
|
207
|
-
<AppstoreOutlined className='iblock font20 control-icon' onClick={openWidgetsModal} />
|
|
208
|
-
</SideIcon>
|
|
209
|
-
|
|
210
161
|
<SideIcon
|
|
211
162
|
title={e('about')}
|
|
212
163
|
active={showInfoModal}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* renders the customizable far-left sidebar icons.
|
|
3
|
+
*
|
|
4
|
+
* which icons appear, and in what order, is driven by `iconIds`
|
|
5
|
+
* (from config.leftSideBarIcons). The "about", "hide sidebar" and
|
|
6
|
+
* "file transfer" icons are not customizable — they are rendered
|
|
7
|
+
* directly by the parent (sidebar/index.jsx).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
AppstoreOutlined,
|
|
12
|
+
BookOutlined,
|
|
13
|
+
CloudSyncOutlined,
|
|
14
|
+
PictureOutlined,
|
|
15
|
+
PlusCircleOutlined,
|
|
16
|
+
SettingOutlined,
|
|
17
|
+
ThunderboltOutlined
|
|
18
|
+
} from '@ant-design/icons'
|
|
19
|
+
import { Badge, Popover } from 'antd'
|
|
20
|
+
import SideIcon from './side-icon'
|
|
21
|
+
import QuickConnect from '../tabs/quick-connect'
|
|
22
|
+
import { settingMap } from '../../common/constants'
|
|
23
|
+
import { defaultLeftSideBarIcons } from '../../common/left-sidebar-icon-defs'
|
|
24
|
+
|
|
25
|
+
const e = window.translate
|
|
26
|
+
|
|
27
|
+
export default function LeftSidebarIcons (props) {
|
|
28
|
+
const {
|
|
29
|
+
iconIds,
|
|
30
|
+
handleClickBookmark,
|
|
31
|
+
onNewSsh,
|
|
32
|
+
openSetting,
|
|
33
|
+
openSettingSync,
|
|
34
|
+
openTerminalThemes,
|
|
35
|
+
openWidgetsModal,
|
|
36
|
+
bookmarksActive,
|
|
37
|
+
themeActive,
|
|
38
|
+
settingActive,
|
|
39
|
+
syncActive,
|
|
40
|
+
widgetsActive,
|
|
41
|
+
isSyncingSetting,
|
|
42
|
+
widgetInstancesLength
|
|
43
|
+
} = props
|
|
44
|
+
|
|
45
|
+
const ids = Array.isArray(iconIds) && iconIds.length
|
|
46
|
+
? iconIds
|
|
47
|
+
: defaultLeftSideBarIcons
|
|
48
|
+
|
|
49
|
+
const registry = {
|
|
50
|
+
newBookmark: (
|
|
51
|
+
<SideIcon
|
|
52
|
+
key='newBookmark'
|
|
53
|
+
title={e('newBookmark')}
|
|
54
|
+
>
|
|
55
|
+
<PlusCircleOutlined
|
|
56
|
+
className='font22 iblock control-icon'
|
|
57
|
+
onClick={onNewSsh}
|
|
58
|
+
/>
|
|
59
|
+
</SideIcon>
|
|
60
|
+
),
|
|
61
|
+
quickConnect: (
|
|
62
|
+
<Popover
|
|
63
|
+
key='quickConnect'
|
|
64
|
+
content={<QuickConnect inputOnly />}
|
|
65
|
+
trigger='click'
|
|
66
|
+
placement='right'
|
|
67
|
+
>
|
|
68
|
+
<div
|
|
69
|
+
className='control-icon-wrap'
|
|
70
|
+
title={e('quickConnect')}
|
|
71
|
+
>
|
|
72
|
+
<ThunderboltOutlined className='font20 iblock control-icon' />
|
|
73
|
+
</div>
|
|
74
|
+
</Popover>
|
|
75
|
+
),
|
|
76
|
+
bookmarks: (
|
|
77
|
+
<SideIcon
|
|
78
|
+
key='bookmarks'
|
|
79
|
+
title={e(settingMap.bookmarks)}
|
|
80
|
+
active={bookmarksActive}
|
|
81
|
+
>
|
|
82
|
+
<BookOutlined
|
|
83
|
+
onClick={handleClickBookmark}
|
|
84
|
+
className='font20 iblock control-icon'
|
|
85
|
+
/>
|
|
86
|
+
</SideIcon>
|
|
87
|
+
),
|
|
88
|
+
terminalThemes: (
|
|
89
|
+
<SideIcon
|
|
90
|
+
key='terminalThemes'
|
|
91
|
+
title={e(settingMap.terminalThemes)}
|
|
92
|
+
active={themeActive}
|
|
93
|
+
>
|
|
94
|
+
<PictureOutlined
|
|
95
|
+
className='font20 iblock pointer control-icon'
|
|
96
|
+
onClick={openTerminalThemes}
|
|
97
|
+
/>
|
|
98
|
+
</SideIcon>
|
|
99
|
+
),
|
|
100
|
+
setting: (
|
|
101
|
+
<SideIcon
|
|
102
|
+
key='setting'
|
|
103
|
+
title={e(settingMap.setting)}
|
|
104
|
+
active={settingActive}
|
|
105
|
+
>
|
|
106
|
+
<SettingOutlined
|
|
107
|
+
className='iblock font20 control-icon'
|
|
108
|
+
onClick={openSetting}
|
|
109
|
+
/>
|
|
110
|
+
</SideIcon>
|
|
111
|
+
),
|
|
112
|
+
settingSync: (
|
|
113
|
+
<SideIcon
|
|
114
|
+
key='settingSync'
|
|
115
|
+
title={e('settingSync')}
|
|
116
|
+
active={syncActive}
|
|
117
|
+
>
|
|
118
|
+
<CloudSyncOutlined
|
|
119
|
+
className='iblock font20 control-icon'
|
|
120
|
+
onClick={openSettingSync}
|
|
121
|
+
spin={isSyncingSetting}
|
|
122
|
+
/>
|
|
123
|
+
</SideIcon>
|
|
124
|
+
),
|
|
125
|
+
widgets: (
|
|
126
|
+
<SideIcon
|
|
127
|
+
key='widgets'
|
|
128
|
+
title={e('widgets')}
|
|
129
|
+
active={widgetsActive}
|
|
130
|
+
>
|
|
131
|
+
<Badge
|
|
132
|
+
count={widgetInstancesLength}
|
|
133
|
+
size='small'
|
|
134
|
+
offset={[0, 0]}
|
|
135
|
+
color='green'
|
|
136
|
+
overflowCount={99}
|
|
137
|
+
>
|
|
138
|
+
<AppstoreOutlined
|
|
139
|
+
className='iblock font20 control-icon'
|
|
140
|
+
onClick={openWidgetsModal}
|
|
141
|
+
/>
|
|
142
|
+
</Badge>
|
|
143
|
+
</SideIcon>
|
|
144
|
+
)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return ids.map(id => registry[id] || null)
|
|
148
|
+
}
|
|
@@ -10,11 +10,11 @@ import {
|
|
|
10
10
|
ReloadOutlined
|
|
11
11
|
} from '@ant-design/icons'
|
|
12
12
|
import {
|
|
13
|
-
Spin
|
|
14
|
-
Switch
|
|
13
|
+
Spin
|
|
15
14
|
} from 'antd'
|
|
16
15
|
import * as ls from '../../common/safe-local-storage'
|
|
17
16
|
import RemoteFloatControl from '../common/remote-float-control'
|
|
17
|
+
import SwitchLabel from '../common/switch'
|
|
18
18
|
import './spice.styl'
|
|
19
19
|
|
|
20
20
|
async function loadSpiceModule () {
|
|
@@ -132,8 +132,7 @@ export default class SpiceSession extends PureComponent {
|
|
|
132
132
|
const scaleProps = {
|
|
133
133
|
checked: this.state.scaleViewport,
|
|
134
134
|
onChange: this.handleScaleViewChange,
|
|
135
|
-
|
|
136
|
-
checkedChildren: window.translate('scaleViewport'),
|
|
135
|
+
label: window.translate('scaleViewport'),
|
|
137
136
|
className: 'mg1l'
|
|
138
137
|
}
|
|
139
138
|
return (
|
|
@@ -144,7 +143,7 @@ export default class SpiceSession extends PureComponent {
|
|
|
144
143
|
className='mg2r mg1l pointer'
|
|
145
144
|
/>
|
|
146
145
|
{this.renderInfo()}
|
|
147
|
-
<
|
|
146
|
+
<SwitchLabel
|
|
148
147
|
{...scaleProps}
|
|
149
148
|
/>
|
|
150
149
|
</div>
|
|
@@ -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
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shortcut-bar data: the candidate button library, the default selection, and
|
|
3
|
+
* localStorage persistence. Pure data (no React) so it can be shared by the
|
|
4
|
+
* bar (shortcut-bar.jsx) and the edit modal (shortcut-bar-edit.jsx).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as ls from '../../common/safe-local-storage'
|
|
8
|
+
import { shortcutBarLsKey } from '../../common/constants'
|
|
9
|
+
|
|
10
|
+
// build control bytes at runtime — keeps source free of literal control chars
|
|
11
|
+
const ESC = String.fromCharCode(0x1b)
|
|
12
|
+
const DEL = String.fromCharCode(0x7f)
|
|
13
|
+
|
|
14
|
+
// --- candidate library -----------------------------------------------------
|
|
15
|
+
|
|
16
|
+
const ctrlChars = 'abcdefghijklmnopqrstuvwxyz'.split('')
|
|
17
|
+
const ctrlButtons = ctrlChars.map(ch => ({
|
|
18
|
+
id: 'ctrl+' + ch,
|
|
19
|
+
label: 'Ctrl+' + ch.toUpperCase(),
|
|
20
|
+
data: String.fromCharCode(ch.charCodeAt(0) - 96)
|
|
21
|
+
}))
|
|
22
|
+
|
|
23
|
+
const fKeys = [
|
|
24
|
+
['f1', 'F1', ESC + 'OP'],
|
|
25
|
+
['f2', 'F2', ESC + 'OQ'],
|
|
26
|
+
['f3', 'F3', ESC + 'OR'],
|
|
27
|
+
['f4', 'F4', ESC + 'OS'],
|
|
28
|
+
['f5', 'F5', ESC + '[15~'],
|
|
29
|
+
['f6', 'F6', ESC + '[17~'],
|
|
30
|
+
['f7', 'F7', ESC + '[18~'],
|
|
31
|
+
['f8', 'F8', ESC + '[19~'],
|
|
32
|
+
['f9', 'F9', ESC + '[20~'],
|
|
33
|
+
['f10', 'F10', ESC + '[21~'],
|
|
34
|
+
['f11', 'F11', ESC + '[23~'],
|
|
35
|
+
['f12', 'F12', ESC + '[24~']
|
|
36
|
+
].map(([id, label, data]) => ({ id, label, data }))
|
|
37
|
+
|
|
38
|
+
const navButtons = [
|
|
39
|
+
{ id: 'esc', label: 'Esc', data: ESC },
|
|
40
|
+
{ id: 'tab', label: 'Tab', data: '\t' },
|
|
41
|
+
{ id: 'enter', label: 'Enter', data: '\r' },
|
|
42
|
+
{ id: 'space', label: 'Space', data: ' ' },
|
|
43
|
+
{ id: 'backspace', label: 'Bksp', data: DEL },
|
|
44
|
+
{ id: 'arrow-up', label: '↑', data: ESC + '[A' },
|
|
45
|
+
{ id: 'arrow-down', label: '↓', data: ESC + '[B' },
|
|
46
|
+
{ id: 'arrow-right', label: '→', data: ESC + '[C' },
|
|
47
|
+
{ id: 'arrow-left', label: '←', data: ESC + '[D' },
|
|
48
|
+
{ id: 'home', label: 'Home', data: ESC + '[H' },
|
|
49
|
+
{ id: 'end', label: 'End', data: ESC + '[F' },
|
|
50
|
+
{ id: 'pageup', label: 'PgUp', data: ESC + '[5~' },
|
|
51
|
+
{ id: 'pagedown', label: 'PgDn', data: ESC + '[6~' },
|
|
52
|
+
{ id: 'insert', label: 'Ins', data: ESC + '[2~' },
|
|
53
|
+
{ id: 'delete', label: 'Del', data: ESC + '[3~' }
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
const punctChars = ':;/~|\\`\'".,(){}[]<>=+-_*&^%$#@!?'
|
|
57
|
+
const punctButtons = punctChars.split('').map(ch => ({
|
|
58
|
+
id: 'char-' + ch.charCodeAt(0),
|
|
59
|
+
label: ch,
|
|
60
|
+
data: ch
|
|
61
|
+
}))
|
|
62
|
+
|
|
63
|
+
// full library, de-duplicated by id
|
|
64
|
+
const candidates = []
|
|
65
|
+
const seenIds = new Set()
|
|
66
|
+
;[...navButtons, ...ctrlButtons, ...fKeys, ...punctButtons].forEach(b => {
|
|
67
|
+
if (!seenIds.has(b.id)) {
|
|
68
|
+
seenIds.add(b.id)
|
|
69
|
+
candidates.push(b)
|
|
70
|
+
}
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// ids selected by default — mirrors the keys requested in the feature spec
|
|
74
|
+
const DEFAULT_ACTIVE_IDS = [
|
|
75
|
+
'esc',
|
|
76
|
+
'tab',
|
|
77
|
+
'ctrl+c',
|
|
78
|
+
'ctrl+v',
|
|
79
|
+
'ctrl+z',
|
|
80
|
+
'ctrl+y',
|
|
81
|
+
'ctrl+a',
|
|
82
|
+
'ctrl+x',
|
|
83
|
+
'ctrl+s',
|
|
84
|
+
'ctrl+f',
|
|
85
|
+
'ctrl+r',
|
|
86
|
+
'ctrl+g',
|
|
87
|
+
'ctrl+h',
|
|
88
|
+
'ctrl+n',
|
|
89
|
+
'ctrl+p',
|
|
90
|
+
'ctrl+l',
|
|
91
|
+
'enter',
|
|
92
|
+
'char-58',
|
|
93
|
+
'arrow-up',
|
|
94
|
+
'arrow-down',
|
|
95
|
+
'arrow-left',
|
|
96
|
+
'arrow-right',
|
|
97
|
+
'f1',
|
|
98
|
+
'f2',
|
|
99
|
+
'f3',
|
|
100
|
+
'f4',
|
|
101
|
+
'f5',
|
|
102
|
+
'f6',
|
|
103
|
+
'f7',
|
|
104
|
+
'f8',
|
|
105
|
+
'f9',
|
|
106
|
+
'f10',
|
|
107
|
+
'f11',
|
|
108
|
+
'f12'
|
|
109
|
+
]
|
|
110
|
+
|
|
111
|
+
export function defaultActive () {
|
|
112
|
+
const byId = new Map(candidates.map(c => [c.id, c]))
|
|
113
|
+
return DEFAULT_ACTIVE_IDS
|
|
114
|
+
.map(id => byId.get(id))
|
|
115
|
+
.filter(Boolean)
|
|
116
|
+
.map(b => ({ ...b }))
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function loadActive () {
|
|
120
|
+
const saved = ls.getItemJSON(shortcutBarLsKey, null)
|
|
121
|
+
if (Array.isArray(saved) && saved.length) {
|
|
122
|
+
return saved
|
|
123
|
+
}
|
|
124
|
+
return defaultActive()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function saveActive (arr) {
|
|
128
|
+
ls.setItemJSON(shortcutBarLsKey, arr)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export { candidates }
|