@electerm/electerm-react 1.101.20 → 2.1.16
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 +6 -0
- package/client/common/init-setting-item.js +1 -1
- package/client/components/quick-commands/quick-commands-form-elem.jsx +7 -7
- package/client/components/setting-panel/list.jsx +14 -1
- package/client/components/sftp/paged-list.jsx +1 -1
- package/client/components/shortcuts/shortcut-control.jsx +8 -0
- package/client/components/shortcuts/shortcuts-defaults.js +5 -0
- package/client/components/sys-menu/menu-btn.jsx +19 -14
- package/client/components/tabs/index.jsx +15 -6
- package/client/components/tabs/no-session.jsx +7 -2
- package/client/components/tabs/tab.jsx +8 -2
- package/client/components/theme/theme-list.jsx +35 -13
- package/client/components/tree-list/tree-list.jsx +4 -0
- package/client/store/db-upgrade.js +41 -17
- package/client/store/init-state.js +1 -0
- package/client/store/store.js +8 -5
- package/client/store/tab.js +10 -0
- package/client/store/watch.js +5 -1
- package/package.json +1 -1
|
@@ -107,6 +107,12 @@ export const settingMap = buildConst([
|
|
|
107
107
|
'profiles'
|
|
108
108
|
])
|
|
109
109
|
|
|
110
|
+
export const staticNewItemTabs = new Set([
|
|
111
|
+
'terminalThemes',
|
|
112
|
+
'quickCommands',
|
|
113
|
+
'profiles'
|
|
114
|
+
])
|
|
115
|
+
|
|
110
116
|
export const infoTabs = buildConst([
|
|
111
117
|
'info',
|
|
112
118
|
'deps',
|
|
@@ -138,13 +138,6 @@ export default function QuickCommandForm (props) {
|
|
|
138
138
|
const wiki = 'https://github.com/electerm/electerm/wiki/quick-command-templates'
|
|
139
139
|
return (
|
|
140
140
|
<>
|
|
141
|
-
<p>
|
|
142
|
-
<b className='mg1r'>{e('templates')}:</b>
|
|
143
|
-
<span className='mg1r'>{templatesStr}</span>
|
|
144
|
-
<HelpIcon
|
|
145
|
-
link={wiki}
|
|
146
|
-
/>
|
|
147
|
-
</p>
|
|
148
141
|
<Form
|
|
149
142
|
form={form}
|
|
150
143
|
onFinish={handleSubmit}
|
|
@@ -210,6 +203,13 @@ export default function QuickCommandForm (props) {
|
|
|
210
203
|
</Button>
|
|
211
204
|
</p>
|
|
212
205
|
</FormItem>
|
|
206
|
+
<p>
|
|
207
|
+
<b className='mg1r'>{e('templates')}:</b>
|
|
208
|
+
<span className='mg1r'>{templatesStr}</span>
|
|
209
|
+
<HelpIcon
|
|
210
|
+
link={wiki}
|
|
211
|
+
/>
|
|
212
|
+
</p>
|
|
213
213
|
</Form>
|
|
214
214
|
</>
|
|
215
215
|
)
|
|
@@ -9,7 +9,8 @@ import createName, { createTitleTag } from '../../common/create-title'
|
|
|
9
9
|
import classnames from 'classnames'
|
|
10
10
|
import { noop } from 'lodash-es'
|
|
11
11
|
import highlight from '../common/highlight'
|
|
12
|
-
import { settingSyncId, settingCommonId } from '../../common/constants'
|
|
12
|
+
import { settingSyncId, settingCommonId, staticNewItemTabs } from '../../common/constants'
|
|
13
|
+
import getInitItem from '../../common/init-setting-item'
|
|
13
14
|
import './list.styl'
|
|
14
15
|
|
|
15
16
|
const e = window.translate
|
|
@@ -95,6 +96,17 @@ export default class ItemList extends React.PureComponent {
|
|
|
95
96
|
return icon
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
renderNewItem () {
|
|
100
|
+
const { type } = this.props
|
|
101
|
+
|
|
102
|
+
if (!staticNewItemTabs.has(type)) {
|
|
103
|
+
return null
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const newItem = getInitItem([], type)
|
|
107
|
+
return this.renderItem(newItem, -1)
|
|
108
|
+
}
|
|
109
|
+
|
|
98
110
|
renderItem = (item, i) => {
|
|
99
111
|
const { onClickItem, type, activeItemId } = this.props
|
|
100
112
|
const { id } = item
|
|
@@ -175,6 +187,7 @@ export default class ItemList extends React.PureComponent {
|
|
|
175
187
|
{this.renderLabels ? this.renderLabels() : null}
|
|
176
188
|
{this.renderSearch()}
|
|
177
189
|
<div className='item-list-wrap' style={listStyle}>
|
|
190
|
+
{this.renderNewItem()}
|
|
178
191
|
{
|
|
179
192
|
list.map(this.renderItem)
|
|
180
193
|
}
|
|
@@ -144,6 +144,14 @@ class ShortcutControl extends React.PureComponent {
|
|
|
144
144
|
window.store.cloneToNextLayout()
|
|
145
145
|
}, 500)
|
|
146
146
|
|
|
147
|
+
duplicateTabShortcut = throttle((e) => {
|
|
148
|
+
e.stopPropagation()
|
|
149
|
+
const { activeTabId } = window.store
|
|
150
|
+
if (activeTabId) {
|
|
151
|
+
window.store.duplicateTab(activeTabId)
|
|
152
|
+
}
|
|
153
|
+
}, 500)
|
|
154
|
+
|
|
147
155
|
prevTabShortcut = throttle((e) => {
|
|
148
156
|
e.stopPropagation()
|
|
149
157
|
window.store.clickPrevTab()
|
|
@@ -64,27 +64,31 @@ class MenuBtn extends PureComponent {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
renderContext = () => {
|
|
67
|
-
|
|
67
|
+
const items = [
|
|
68
68
|
{
|
|
69
69
|
func: 'onNewSsh',
|
|
70
70
|
icon: 'CodeFilled',
|
|
71
71
|
text: e('newBookmark'),
|
|
72
72
|
subText: this.getShortcut('app_newBookmark')
|
|
73
|
-
}
|
|
74
|
-
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
if (window.store.hasNodePty) {
|
|
76
|
+
items.push({
|
|
75
77
|
func: 'addTab',
|
|
76
78
|
icon: 'RightSquareFilled',
|
|
77
79
|
text: e('newTab')
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
})
|
|
81
|
+
}
|
|
82
|
+
// {
|
|
83
|
+
// type: 'hr'
|
|
84
|
+
// },
|
|
85
|
+
items.push({
|
|
86
|
+
noCloseMenu: true,
|
|
87
|
+
icon: 'BookOutlined',
|
|
88
|
+
text: e('bookmarks'),
|
|
89
|
+
submenu: 'Bookmark'
|
|
90
|
+
})
|
|
91
|
+
items.push(
|
|
88
92
|
{
|
|
89
93
|
noCloseMenu: true,
|
|
90
94
|
icon: 'ClockCircleOutlined',
|
|
@@ -162,7 +166,8 @@ class MenuBtn extends PureComponent {
|
|
|
162
166
|
icon: 'CloseOutlined',
|
|
163
167
|
text: e('close')
|
|
164
168
|
}
|
|
165
|
-
|
|
169
|
+
)
|
|
170
|
+
return items
|
|
166
171
|
}
|
|
167
172
|
|
|
168
173
|
renderMenu () {
|
|
@@ -124,6 +124,10 @@ export default class Tabs extends Component {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
handleTabAdd = () => {
|
|
127
|
+
if (!window.store.hasNodePty) {
|
|
128
|
+
window.store.onNewSsh()
|
|
129
|
+
return
|
|
130
|
+
}
|
|
127
131
|
window.store.addTab(
|
|
128
132
|
undefined, undefined,
|
|
129
133
|
this.props.batch
|
|
@@ -195,6 +199,16 @@ export default class Tabs extends Component {
|
|
|
195
199
|
renderMenus () {
|
|
196
200
|
const { onNewSsh } = window.store
|
|
197
201
|
const cls = 'pd2x pd1y context-item pointer'
|
|
202
|
+
const addTabBtn = window.store.hasNodePty
|
|
203
|
+
? (
|
|
204
|
+
<div
|
|
205
|
+
className={cls}
|
|
206
|
+
onClick={this.handleTabAdd}
|
|
207
|
+
>
|
|
208
|
+
<RightSquareFilled /> {e('newTab')}
|
|
209
|
+
</div>
|
|
210
|
+
)
|
|
211
|
+
: null
|
|
198
212
|
return (
|
|
199
213
|
<div
|
|
200
214
|
className='add-menu-wrap' style={{
|
|
@@ -207,12 +221,7 @@ export default class Tabs extends Component {
|
|
|
207
221
|
>
|
|
208
222
|
<CodeFilled /> {e('newBookmark')}
|
|
209
223
|
</div>
|
|
210
|
-
|
|
211
|
-
className={cls}
|
|
212
|
-
onClick={this.handleTabAdd}
|
|
213
|
-
>
|
|
214
|
-
<RightSquareFilled /> {e('newTab')}
|
|
215
|
-
</div>
|
|
224
|
+
{addTabBtn}
|
|
216
225
|
<BookmarksList
|
|
217
226
|
store={window.store}
|
|
218
227
|
/>
|
|
@@ -13,8 +13,8 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
|
|
|
13
13
|
const handleClick = () => {
|
|
14
14
|
window.openTabBatch = batch
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
const newTabDom = window.store.hasNodePty
|
|
17
|
+
? (
|
|
18
18
|
<Button
|
|
19
19
|
onClick={onNewTab}
|
|
20
20
|
size='large'
|
|
@@ -22,6 +22,11 @@ export default function NoSessionPanel ({ height, onNewTab, onNewSsh, batch }) {
|
|
|
22
22
|
>
|
|
23
23
|
{e('newTab')}
|
|
24
24
|
</Button>
|
|
25
|
+
)
|
|
26
|
+
: null
|
|
27
|
+
return (
|
|
28
|
+
<div className='no-sessions electerm-logo-bg' {...props}>
|
|
29
|
+
{newTabDom}
|
|
25
30
|
<Button
|
|
26
31
|
onClick={onNewSsh}
|
|
27
32
|
size='large'
|
|
@@ -220,7 +220,11 @@ class Tab extends Component {
|
|
|
220
220
|
}
|
|
221
221
|
|
|
222
222
|
newTab = () => {
|
|
223
|
-
|
|
223
|
+
if (window.store.hasNodePty) {
|
|
224
|
+
this.props.addTab()
|
|
225
|
+
} else {
|
|
226
|
+
window.store.onNewSsh()
|
|
227
|
+
}
|
|
224
228
|
}
|
|
225
229
|
|
|
226
230
|
doRename = () => {
|
|
@@ -264,6 +268,7 @@ class Tab extends Component {
|
|
|
264
268
|
const reloadShortcut = this.getShortcut('app_reloadCurrentTab')
|
|
265
269
|
const closeShortcut = this.getShortcut('app_closeCurrentTab')
|
|
266
270
|
const cloneToNextShortcut = this.getShortcut('app_cloneToNextLayout')
|
|
271
|
+
const duplicateShortcut = this.getShortcut('app_duplicateTab')
|
|
267
272
|
|
|
268
273
|
const x = [
|
|
269
274
|
{
|
|
@@ -290,7 +295,8 @@ class Tab extends Component {
|
|
|
290
295
|
{
|
|
291
296
|
key: 'handleDup',
|
|
292
297
|
icon: <iconsMap.CopyOutlined />,
|
|
293
|
-
label: e('duplicate')
|
|
298
|
+
label: e('duplicate'),
|
|
299
|
+
extra: duplicateShortcut
|
|
294
300
|
},
|
|
295
301
|
{
|
|
296
302
|
key: 'cloneToNextLayout',
|
|
@@ -7,7 +7,8 @@ import { LoadingOutlined, CheckCircleOutlined } from '@ant-design/icons'
|
|
|
7
7
|
import { pick } from 'lodash-es'
|
|
8
8
|
import { Pagination } from 'antd'
|
|
9
9
|
import ThemeListItem from './theme-list-item'
|
|
10
|
-
import { defaultTheme } from '../../common/constants'
|
|
10
|
+
import { defaultTheme, settingMap } from '../../common/constants'
|
|
11
|
+
import getInitItem from '../../common/init-setting-item'
|
|
11
12
|
import './terminal-theme-list.styl'
|
|
12
13
|
|
|
13
14
|
const e = window.translate
|
|
@@ -58,13 +59,33 @@ export default class ThemeList extends List {
|
|
|
58
59
|
)
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
renderNewItem () {
|
|
63
|
+
const newThemeItem = getInitItem([], settingMap.terminalThemes)
|
|
64
|
+
const itemProps = {
|
|
65
|
+
item: newThemeItem,
|
|
66
|
+
renderDelBtn: this.renderDelBtn,
|
|
67
|
+
activeItemId: this.props.activeItemId,
|
|
68
|
+
...pick(
|
|
69
|
+
this.props,
|
|
70
|
+
[
|
|
71
|
+
'onClickItem',
|
|
72
|
+
'theme',
|
|
73
|
+
'keyword'
|
|
74
|
+
]
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
return (
|
|
78
|
+
<ThemeListItem key='new-theme' {...itemProps} />
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
|
|
61
82
|
filter = list => {
|
|
62
|
-
const { keyword
|
|
83
|
+
const { keyword } = this.state
|
|
63
84
|
return keyword
|
|
64
|
-
? list.
|
|
85
|
+
? list.filter(item => {
|
|
65
86
|
return item.name.toLowerCase().includes(keyword.toLowerCase())
|
|
66
87
|
})
|
|
67
|
-
: list
|
|
88
|
+
: list
|
|
68
89
|
}
|
|
69
90
|
|
|
70
91
|
paged = list => {
|
|
@@ -77,6 +98,13 @@ export default class ThemeList extends List {
|
|
|
77
98
|
|
|
78
99
|
render () {
|
|
79
100
|
const { ready, page, pageSize } = this.state
|
|
101
|
+
if (!ready) {
|
|
102
|
+
return (
|
|
103
|
+
<div className='pd3 aligncenter'>
|
|
104
|
+
<LoadingOutlined />
|
|
105
|
+
</div>
|
|
106
|
+
)
|
|
107
|
+
}
|
|
80
108
|
let {
|
|
81
109
|
list = [],
|
|
82
110
|
type,
|
|
@@ -92,6 +120,7 @@ export default class ThemeList extends List {
|
|
|
92
120
|
{this.renderSearch()}
|
|
93
121
|
{this.renderCurrentTheme()}
|
|
94
122
|
<div className='item-list-wrap' style={listStyle}>
|
|
123
|
+
{this.renderNewItem()}
|
|
95
124
|
{
|
|
96
125
|
list.map(this.renderItem)
|
|
97
126
|
}
|
|
@@ -101,17 +130,10 @@ export default class ThemeList extends List {
|
|
|
101
130
|
total={all}
|
|
102
131
|
current={page}
|
|
103
132
|
pageSize={pageSize}
|
|
133
|
+
showLessItems
|
|
134
|
+
simple
|
|
104
135
|
onShowSizeChange={this.handlePageSizeChange}
|
|
105
136
|
/>
|
|
106
|
-
{
|
|
107
|
-
ready
|
|
108
|
-
? null
|
|
109
|
-
: (
|
|
110
|
-
<div className='pd3 aligncenter'>
|
|
111
|
-
<LoadingOutlined />
|
|
112
|
-
</div>
|
|
113
|
-
)
|
|
114
|
-
}
|
|
115
137
|
</div>
|
|
116
138
|
)
|
|
117
139
|
}
|
|
@@ -309,6 +309,10 @@ export default class ItemListTree extends Component {
|
|
|
309
309
|
store.storeAssign({
|
|
310
310
|
currentBookmarkGroupId: id
|
|
311
311
|
})
|
|
312
|
+
const func = this.props.expandedKeys.includes(id)
|
|
313
|
+
? this.onUnExpandKey
|
|
314
|
+
: this.onExpandKey
|
|
315
|
+
func({ id })
|
|
312
316
|
} else {
|
|
313
317
|
store.storeAssign({
|
|
314
318
|
currentBookmarkGroupId: findBookmarkGroupId(store.bookmarkGroups, id)
|
|
@@ -12,31 +12,55 @@ export default (Store) => {
|
|
|
12
12
|
return false
|
|
13
13
|
}
|
|
14
14
|
const shouldUpgrade = await window.pre.runGlobalAsync('checkDbUpgrade')
|
|
15
|
-
|
|
15
|
+
const shouldMigrate = await window.pre.runGlobalAsync('checkMigrate')
|
|
16
|
+
if (!shouldUpgrade && !shouldMigrate) {
|
|
17
|
+
window.migrating = false
|
|
16
18
|
return false
|
|
17
19
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
} = shouldUpgrade
|
|
22
|
-
const mod = Modal.info({
|
|
23
|
-
title: 'Upgrading database',
|
|
24
|
-
content: `Upgrading database... from v${dbVersion} to v${packVersion} please wait`,
|
|
20
|
+
window.migrating = true
|
|
21
|
+
let mod
|
|
22
|
+
const commonProps = {
|
|
25
23
|
keyboard: false,
|
|
26
24
|
okButtonProps: {
|
|
27
25
|
style: {
|
|
28
26
|
display: 'none'
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
29
|
+
}
|
|
30
|
+
if (shouldMigrate) {
|
|
31
|
+
mod = Modal.info({
|
|
32
|
+
title: 'Migrating database',
|
|
33
|
+
content: 'Migrating database... please wait',
|
|
34
|
+
...commonProps
|
|
35
|
+
})
|
|
36
|
+
await window.pre.runGlobalAsync('migrate')
|
|
37
|
+
mod.update({
|
|
38
|
+
title: 'Done',
|
|
39
|
+
content: 'Database Migrated',
|
|
40
|
+
okButtonProps: {}
|
|
41
|
+
})
|
|
42
|
+
await delay(2000)
|
|
43
|
+
mod.destroy()
|
|
44
|
+
}
|
|
45
|
+
if (shouldUpgrade) {
|
|
46
|
+
const {
|
|
47
|
+
dbVersion,
|
|
48
|
+
packVersion
|
|
49
|
+
} = shouldUpgrade
|
|
50
|
+
mod = Modal.info({
|
|
51
|
+
title: 'Upgrading database',
|
|
52
|
+
content: `Upgrading database... from v${dbVersion} to v${packVersion} please wait`,
|
|
53
|
+
...commonProps
|
|
54
|
+
})
|
|
55
|
+
await window.pre.runGlobalAsync('doUpgrade')
|
|
56
|
+
mod.update({
|
|
57
|
+
title: 'Done',
|
|
58
|
+
content: 'Database Upgraded',
|
|
59
|
+
okButtonProps: {}
|
|
60
|
+
})
|
|
61
|
+
await delay(2000)
|
|
62
|
+
mod.destroy()
|
|
63
|
+
}
|
|
40
64
|
await store.restart()
|
|
41
65
|
return true
|
|
42
66
|
}
|
|
@@ -188,6 +188,7 @@ export default () => {
|
|
|
188
188
|
innerWidth: window.innerWidth,
|
|
189
189
|
height: 500,
|
|
190
190
|
isMaximized: window.pre.runSync('isMaximized'),
|
|
191
|
+
hasNodePty: window.pre.runSync('nodePtyCheck'),
|
|
191
192
|
terminalFullScreen: false,
|
|
192
193
|
hideDelKeyTip: ls.getItem(dismissDelKeyTipLsKey) === 'y',
|
|
193
194
|
tabsHeight: 36
|
package/client/store/store.js
CHANGED
|
@@ -32,7 +32,8 @@ import getBrand from '../components/ai/get-brand'
|
|
|
32
32
|
import {
|
|
33
33
|
settingMap,
|
|
34
34
|
terminalSshConfigType,
|
|
35
|
-
paneMap
|
|
35
|
+
paneMap,
|
|
36
|
+
staticNewItemTabs
|
|
36
37
|
} from '../common/constants'
|
|
37
38
|
import getInitItem from '../common/init-setting-item'
|
|
38
39
|
import createTitle from '../common/create-title'
|
|
@@ -149,10 +150,12 @@ class Store {
|
|
|
149
150
|
const initItem = getInitItem(arr, settingTab)
|
|
150
151
|
return settingTab === settingMap.history
|
|
151
152
|
? arr
|
|
152
|
-
:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
: staticNewItemTabs.has(settingTab)
|
|
154
|
+
? arr // Don't add initItem for these tabs, they will be handled separately
|
|
155
|
+
: [
|
|
156
|
+
deepCopy(initItem),
|
|
157
|
+
...arr
|
|
158
|
+
]
|
|
156
159
|
}
|
|
157
160
|
|
|
158
161
|
get terminalCommandSuggestions () {
|
package/client/store/tab.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
maxHistory
|
|
11
11
|
} from '../common/constants'
|
|
12
12
|
import { refs } from '../components/common/ref'
|
|
13
|
+
import { message } from 'antd'
|
|
13
14
|
import * as ls from '../common/safe-local-storage'
|
|
14
15
|
import deepCopy from 'json-deep-copy'
|
|
15
16
|
import generate from '../common/id-with-stamp'
|
|
@@ -312,6 +313,15 @@ export default Store => {
|
|
|
312
313
|
index,
|
|
313
314
|
batch
|
|
314
315
|
) {
|
|
316
|
+
if (
|
|
317
|
+
(!newTab.type || newTab.type === 'local') &&
|
|
318
|
+
!newTab.host &&
|
|
319
|
+
!window.store.hasNodePty
|
|
320
|
+
) {
|
|
321
|
+
return message.warning(
|
|
322
|
+
'local terminal is not supported, due to node-pty not working in this build'
|
|
323
|
+
)
|
|
324
|
+
}
|
|
315
325
|
const { store } = window
|
|
316
326
|
const { tabs } = store
|
|
317
327
|
newTab.tabCount = store.nextTabCount()
|
package/client/store/watch.js
CHANGED
|
@@ -40,6 +40,9 @@ export default store => {
|
|
|
40
40
|
|
|
41
41
|
for (const name of dbNamesForWatch) {
|
|
42
42
|
window[`watch${name}`] = autoRun(async () => {
|
|
43
|
+
if (window.migrating) {
|
|
44
|
+
return
|
|
45
|
+
}
|
|
43
46
|
const old = refsStatic.get('oldState-' + name)
|
|
44
47
|
const n = store.getItems(name)
|
|
45
48
|
const { updated, added, removed } = dataCompare(
|
|
@@ -55,9 +58,10 @@ export default store => {
|
|
|
55
58
|
for (const item of added) {
|
|
56
59
|
await insert(name, item)
|
|
57
60
|
}
|
|
61
|
+
const newOrder = (n || []).map(d => d.id)
|
|
58
62
|
await update(
|
|
59
63
|
`${name}:order`,
|
|
60
|
-
|
|
64
|
+
newOrder
|
|
61
65
|
)
|
|
62
66
|
refsStatic.add('oldState-' + name, deepCopy(n) || [])
|
|
63
67
|
if (name === 'bookmarks') {
|