@electerm/electerm-react 2.3.151 → 2.3.166
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 +4 -2
- package/client/common/db.js +2 -1
- package/client/common/init-setting-item.js +7 -0
- package/client/components/common/modal.jsx +89 -0
- package/client/components/common/modal.styl +77 -0
- package/client/components/common/notification-with-details.jsx +34 -0
- package/client/components/file-transfer/conflict-resolve.jsx +2 -1
- package/client/components/file-transfer/transfer-speed-format.js +6 -0
- package/client/components/file-transfer/transfer.jsx +5 -2
- package/client/components/file-transfer/transports-action-store.jsx +14 -1
- package/client/components/main/main.jsx +2 -0
- package/client/components/setting-panel/setting-common.jsx +4 -3
- package/client/components/setting-panel/start-session-select.jsx +146 -21
- package/client/components/setting-panel/text-bg-modal.jsx +15 -4
- package/client/components/sftp/file-info-modal.jsx +2 -1
- package/client/components/sftp/file-item.jsx +2 -0
- package/client/components/sidebar/info-modal.jsx +53 -34
- package/client/components/sidebar/info.styl +0 -7
- package/client/components/tabs/index.jsx +6 -58
- package/client/components/tabs/layout-menu.jsx +75 -0
- package/client/components/tabs/layout-select.jsx +60 -0
- package/client/components/tabs/tabs.styl +64 -0
- package/client/components/tabs/workspace-save-modal.jsx +117 -0
- package/client/components/tabs/workspace-select.jsx +79 -0
- package/client/components/terminal/attach-addon-custom.js +7 -1
- package/client/components/terminal/terminal-interactive.jsx +2 -1
- package/client/components/terminal/terminal.jsx +0 -1
- package/client/components/text-editor/text-editor.jsx +2 -1
- package/client/components/tree-list/move-item-modal.jsx +2 -1
- package/client/components/vnc/vnc-session.jsx +2 -2
- package/client/components/widgets/widget-control.jsx +4 -5
- package/client/components/widgets/widget-form.jsx +3 -8
- package/client/components/widgets/widget-instance.jsx +44 -9
- package/client/components/widgets/widget-notification-with-details.jsx +34 -0
- package/client/css/basic.styl +3 -1
- package/client/store/init-state.js +4 -0
- package/client/store/load-data.js +15 -6
- package/client/store/store.js +2 -0
- package/client/store/workspace.js +108 -0
- package/package.json +1 -1
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
InfoCircleOutlined,
|
|
9
9
|
AlignLeftOutlined,
|
|
10
10
|
BugOutlined,
|
|
11
|
-
HeartOutlined
|
|
12
|
-
JavaScriptOutlined
|
|
11
|
+
HeartOutlined
|
|
13
12
|
} from '@ant-design/icons'
|
|
14
|
-
import {
|
|
13
|
+
import { Tabs, Button } from 'antd'
|
|
14
|
+
import Modal from '../common/modal'
|
|
15
15
|
import Link from '../common/external-link'
|
|
16
16
|
import LogoElem from '../common/logo-elem'
|
|
17
17
|
import RunningTime from './app-running-time'
|
|
@@ -55,8 +55,48 @@ export default auto(function InfoModal (props) {
|
|
|
55
55
|
)
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
const
|
|
59
|
-
|
|
58
|
+
const renderParsed = (obj, depth = 0) => {
|
|
59
|
+
if (Array.isArray(obj)) {
|
|
60
|
+
return (
|
|
61
|
+
<ul className='pd2l'>
|
|
62
|
+
{obj.map((item, i) => (
|
|
63
|
+
<li key={i}>{renderParsed(item, depth + 1)}</li>
|
|
64
|
+
))}
|
|
65
|
+
</ul>
|
|
66
|
+
)
|
|
67
|
+
} else if (typeof obj === 'object' && obj !== null) {
|
|
68
|
+
return (
|
|
69
|
+
<div className={depth > 0 ? 'pd2l' : ''}>
|
|
70
|
+
{Object.entries(obj).map(([k, v]) => (
|
|
71
|
+
<div key={k} className='pd1b'>
|
|
72
|
+
<b>{k}:</b> {renderParsed(v, depth + 1)}
|
|
73
|
+
</div>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
)
|
|
77
|
+
} else {
|
|
78
|
+
return <span>{String(obj)}</span>
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const renderValue = (v) => {
|
|
83
|
+
try {
|
|
84
|
+
const parsed = JSON.parse(v)
|
|
85
|
+
return renderParsed(parsed)
|
|
86
|
+
} catch {
|
|
87
|
+
return <span>{v}</span>
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const renderOSInfo = () => {
|
|
92
|
+
return window.pre.osInfo().map(({ k, v }, i) => (
|
|
93
|
+
<div className='pd1b' key={i + '_os_' + k}>
|
|
94
|
+
<b className='bold'>{k}:</b>
|
|
95
|
+
<span className='mg1l'>
|
|
96
|
+
{renderValue(v)}
|
|
97
|
+
</span>
|
|
98
|
+
</div>
|
|
99
|
+
))
|
|
60
100
|
}
|
|
61
101
|
|
|
62
102
|
const { infoModalTab, commandLineHelp } = props
|
|
@@ -100,7 +140,7 @@ export default auto(function InfoModal (props) {
|
|
|
100
140
|
...env
|
|
101
141
|
}
|
|
102
142
|
const title = (
|
|
103
|
-
<div className='
|
|
143
|
+
<div className='custom-modal-close-confirm-title font16'>
|
|
104
144
|
<InfoCircleOutlined className='font20 mg1r' /> {e('about')} {name}
|
|
105
145
|
</div>
|
|
106
146
|
)
|
|
@@ -108,9 +148,7 @@ export default auto(function InfoModal (props) {
|
|
|
108
148
|
title,
|
|
109
149
|
width: window.innerWidth - 100,
|
|
110
150
|
maskClosable: true,
|
|
111
|
-
okText: e('ok'),
|
|
112
151
|
onCancel: onCloseAbout,
|
|
113
|
-
footer: null,
|
|
114
152
|
open: true,
|
|
115
153
|
wrapClassName: 'info-modal'
|
|
116
154
|
}
|
|
@@ -123,18 +161,18 @@ export default auto(function InfoModal (props) {
|
|
|
123
161
|
<LogoElem />
|
|
124
162
|
<p className='mg2b'>{e('desc')}</p>
|
|
125
163
|
<RunningTime />
|
|
126
|
-
<p className='mg1b'>
|
|
127
|
-
<UserOutlined /> <b className='mg1r'>{e('author')} ➾</b>
|
|
128
|
-
<Link to={authorUrl} className='mg1l'>
|
|
129
|
-
{authorName} ({email})
|
|
130
|
-
</Link>
|
|
131
|
-
</p>
|
|
132
164
|
<p className='mg1b'>
|
|
133
165
|
<HomeOutlined /> <b>{e('homepage')}/{e('download')} ➾</b>
|
|
134
166
|
<Link to={homepage} className='mg1l'>
|
|
135
167
|
{homepage}
|
|
136
168
|
</Link>
|
|
137
169
|
</p>
|
|
170
|
+
<p className='mg1b'>
|
|
171
|
+
<UserOutlined /> <b className='mg1r'>{e('author')} ➾</b>
|
|
172
|
+
<Link to={authorUrl} className='mg1l'>
|
|
173
|
+
{authorName} ({email})
|
|
174
|
+
</Link>
|
|
175
|
+
</p>
|
|
138
176
|
<p className='mg1b'>
|
|
139
177
|
<GithubOutlined /> <b className='mg1r'>github ➾</b>
|
|
140
178
|
<Link to={link} className='mg1l'>
|
|
@@ -180,12 +218,6 @@ export default auto(function InfoModal (props) {
|
|
|
180
218
|
<p className='mg1b'>
|
|
181
219
|
<InfoCircleOutlined /> <b className='mg1r'>{window.store.installSrc}</b>
|
|
182
220
|
</p>
|
|
183
|
-
<p className='mg1b'>
|
|
184
|
-
<JavaScriptOutlined /> <b className='mg1r'>Powered by</b>
|
|
185
|
-
<Link to='https://github.com/tylerlong/manate'>
|
|
186
|
-
manate
|
|
187
|
-
</Link>
|
|
188
|
-
</p>
|
|
189
221
|
{renderCheckUpdate()}
|
|
190
222
|
</>
|
|
191
223
|
)
|
|
@@ -223,20 +255,7 @@ export default auto(function InfoModal (props) {
|
|
|
223
255
|
{
|
|
224
256
|
key: infoTabs.os,
|
|
225
257
|
label: e('os'),
|
|
226
|
-
children:
|
|
227
|
-
return (
|
|
228
|
-
<div className='pd1b' key={i + '_os_' + k}>
|
|
229
|
-
<b className='bold'>{k}</b>:
|
|
230
|
-
<span className='mg1l'>
|
|
231
|
-
{
|
|
232
|
-
v.length > 30
|
|
233
|
-
? <pre>{formatJSON(v)}</pre>
|
|
234
|
-
: v
|
|
235
|
-
}
|
|
236
|
-
</span>
|
|
237
|
-
</div>
|
|
238
|
-
)
|
|
239
|
-
})
|
|
258
|
+
children: <div>{renderOSInfo()}</div>
|
|
240
259
|
}
|
|
241
260
|
]
|
|
242
261
|
|
|
@@ -12,26 +12,16 @@ import {
|
|
|
12
12
|
LeftOutlined,
|
|
13
13
|
RightOutlined
|
|
14
14
|
} from '@ant-design/icons'
|
|
15
|
-
import {
|
|
16
|
-
SingleIcon,
|
|
17
|
-
TwoColumnsIcon,
|
|
18
|
-
ThreeColumnsIcon,
|
|
19
|
-
TwoRowsIcon,
|
|
20
|
-
ThreeRowsIcon,
|
|
21
|
-
Grid2x2Icon,
|
|
22
|
-
TwoRowsRightIcon,
|
|
23
|
-
TwoColumnsBottomIcon
|
|
24
|
-
} from '../icons/split-icons'
|
|
25
15
|
import { Dropdown } from 'antd'
|
|
26
16
|
import Tab from './tab'
|
|
17
|
+
import LayoutMenu from './layout-menu'
|
|
27
18
|
import './tabs.styl'
|
|
28
19
|
import {
|
|
29
20
|
tabWidth,
|
|
30
21
|
tabMargin,
|
|
31
22
|
extraTabWidth,
|
|
32
23
|
windowControlWidth,
|
|
33
|
-
isMacJs
|
|
34
|
-
splitMapDesc
|
|
24
|
+
isMacJs
|
|
35
25
|
} from '../../common/constants'
|
|
36
26
|
import WindowControl from './window-control'
|
|
37
27
|
import AddBtn from './add-btn'
|
|
@@ -39,8 +29,6 @@ import AppDrag from './app-drag'
|
|
|
39
29
|
import NoSession from './no-session'
|
|
40
30
|
import classNames from 'classnames'
|
|
41
31
|
|
|
42
|
-
const e = window.translate
|
|
43
|
-
|
|
44
32
|
export default class Tabs extends Component {
|
|
45
33
|
constructor (props) {
|
|
46
34
|
super(props)
|
|
@@ -183,10 +171,6 @@ export default class Tabs extends Component {
|
|
|
183
171
|
window.store['activeTabId' + this.props.batch] = id
|
|
184
172
|
}
|
|
185
173
|
|
|
186
|
-
handleChangeLayout = ({ key }) => {
|
|
187
|
-
window.store.setLayout(key)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
174
|
renderAddBtn = () => {
|
|
191
175
|
const cls = classNames(
|
|
192
176
|
'pointer tabs-add-btn font16',
|
|
@@ -324,48 +308,12 @@ export default class Tabs extends Component {
|
|
|
324
308
|
)
|
|
325
309
|
}
|
|
326
310
|
|
|
327
|
-
getLayoutIcon = (layout) => {
|
|
328
|
-
const iconMaps = {
|
|
329
|
-
single: SingleIcon,
|
|
330
|
-
twoColumns: TwoColumnsIcon,
|
|
331
|
-
threeColumns: ThreeColumnsIcon,
|
|
332
|
-
twoRows: TwoRowsIcon,
|
|
333
|
-
threeRows: ThreeRowsIcon,
|
|
334
|
-
grid2x2: Grid2x2Icon,
|
|
335
|
-
twoRowsRight: TwoRowsRightIcon,
|
|
336
|
-
twoColumnsBottom: TwoColumnsBottomIcon
|
|
337
|
-
}
|
|
338
|
-
return iconMaps[layout]
|
|
339
|
-
}
|
|
340
|
-
|
|
341
311
|
renderLayoutMenu = () => {
|
|
342
|
-
if (!this.shouldRenderWindowControl()) {
|
|
343
|
-
return null
|
|
344
|
-
}
|
|
345
|
-
const items = Object.keys(splitMapDesc).map((t) => {
|
|
346
|
-
const v = splitMapDesc[t]
|
|
347
|
-
const Icon = this.getLayoutIcon(v)
|
|
348
|
-
return {
|
|
349
|
-
key: t,
|
|
350
|
-
label: (
|
|
351
|
-
<span>
|
|
352
|
-
<Icon /> {e(v)}
|
|
353
|
-
</span>
|
|
354
|
-
),
|
|
355
|
-
onClick: () => this.handleChangeLayout({ key: t })
|
|
356
|
-
}
|
|
357
|
-
})
|
|
358
|
-
const v = splitMapDesc[this.props.layout]
|
|
359
|
-
const Icon = this.getLayoutIcon(v)
|
|
360
312
|
return (
|
|
361
|
-
<
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
<span className='tabs-dd-icon layout-dd-icon mg1l'>
|
|
366
|
-
<Icon /> <DownOutlined />
|
|
367
|
-
</span>
|
|
368
|
-
</Dropdown>
|
|
313
|
+
<LayoutMenu
|
|
314
|
+
layout={this.props.layout}
|
|
315
|
+
visible={this.shouldRenderWindowControl()}
|
|
316
|
+
/>
|
|
369
317
|
)
|
|
370
318
|
}
|
|
371
319
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout and Workspace menu dropdown component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React, { useState } from 'react'
|
|
6
|
+
import { Dropdown, Tabs } from 'antd'
|
|
7
|
+
import {
|
|
8
|
+
DownOutlined,
|
|
9
|
+
AppstoreOutlined,
|
|
10
|
+
LayoutOutlined
|
|
11
|
+
} from '@ant-design/icons'
|
|
12
|
+
import { splitMapDesc } from '../../common/constants'
|
|
13
|
+
import LayoutSelect, { getLayoutIcon } from './layout-select'
|
|
14
|
+
import WorkspaceSelect from './workspace-select'
|
|
15
|
+
import HelpIcon from '../common/help-icon'
|
|
16
|
+
|
|
17
|
+
const e = window.translate
|
|
18
|
+
|
|
19
|
+
export default function LayoutMenu (props) {
|
|
20
|
+
const { layout, visible } = props
|
|
21
|
+
const [activeTab, setActiveTab] = useState('layout')
|
|
22
|
+
|
|
23
|
+
if (!visible) {
|
|
24
|
+
return null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const tabItems = [
|
|
28
|
+
{
|
|
29
|
+
key: 'layout',
|
|
30
|
+
label: (
|
|
31
|
+
<span>
|
|
32
|
+
<LayoutOutlined /> {e('layout')}
|
|
33
|
+
</span>
|
|
34
|
+
)
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
key: 'workspaces',
|
|
38
|
+
label: (
|
|
39
|
+
<span>
|
|
40
|
+
<AppstoreOutlined /> {e('workspaces')}
|
|
41
|
+
<HelpIcon link='https://github.com/electerm/electerm/wiki/Workspace-Feature' />
|
|
42
|
+
</span>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
const v = splitMapDesc[layout]
|
|
48
|
+
const Icon = getLayoutIcon(v)
|
|
49
|
+
|
|
50
|
+
const dropdownContent = (
|
|
51
|
+
<div className='layout-workspace-dropdown'>
|
|
52
|
+
<Tabs
|
|
53
|
+
items={tabItems}
|
|
54
|
+
size='small'
|
|
55
|
+
activeKey={activeTab}
|
|
56
|
+
onChange={setActiveTab}
|
|
57
|
+
/>
|
|
58
|
+
{activeTab === 'layout'
|
|
59
|
+
? <LayoutSelect layout={layout} />
|
|
60
|
+
: <WorkspaceSelect store={window.store} />}
|
|
61
|
+
</div>
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<Dropdown
|
|
66
|
+
popupRender={() => dropdownContent}
|
|
67
|
+
placement='bottomRight'
|
|
68
|
+
trigger={['click']}
|
|
69
|
+
>
|
|
70
|
+
<span className='tabs-dd-icon layout-dd-icon mg1l'>
|
|
71
|
+
<Icon /> <DownOutlined />
|
|
72
|
+
</span>
|
|
73
|
+
</Dropdown>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Layout select content component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react'
|
|
6
|
+
import {
|
|
7
|
+
SingleIcon,
|
|
8
|
+
TwoColumnsIcon,
|
|
9
|
+
ThreeColumnsIcon,
|
|
10
|
+
TwoRowsIcon,
|
|
11
|
+
ThreeRowsIcon,
|
|
12
|
+
Grid2x2Icon,
|
|
13
|
+
TwoRowsRightIcon,
|
|
14
|
+
TwoColumnsBottomIcon
|
|
15
|
+
} from '../icons/split-icons'
|
|
16
|
+
import { splitMapDesc } from '../../common/constants'
|
|
17
|
+
|
|
18
|
+
const e = window.translate
|
|
19
|
+
|
|
20
|
+
const iconMaps = {
|
|
21
|
+
single: SingleIcon,
|
|
22
|
+
twoColumns: TwoColumnsIcon,
|
|
23
|
+
threeColumns: ThreeColumnsIcon,
|
|
24
|
+
twoRows: TwoRowsIcon,
|
|
25
|
+
threeRows: ThreeRowsIcon,
|
|
26
|
+
grid2x2: Grid2x2Icon,
|
|
27
|
+
twoRowsRight: TwoRowsRightIcon,
|
|
28
|
+
twoColumnsBottom: TwoColumnsBottomIcon
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getLayoutIcon (layout) {
|
|
32
|
+
return iconMaps[layout]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export default function LayoutSelect (props) {
|
|
36
|
+
const { layout } = props
|
|
37
|
+
|
|
38
|
+
function handleChangeLayout (key) {
|
|
39
|
+
window.store.setLayout(key)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div className='layout-menu-content'>
|
|
44
|
+
{Object.keys(splitMapDesc).map((t) => {
|
|
45
|
+
const v = splitMapDesc[t]
|
|
46
|
+
const Icon = getLayoutIcon(v)
|
|
47
|
+
const isActive = layout === t
|
|
48
|
+
return (
|
|
49
|
+
<div
|
|
50
|
+
key={t}
|
|
51
|
+
className={`layout-menu-item ${isActive ? 'active' : ''}`}
|
|
52
|
+
onClick={() => handleChangeLayout(t)}
|
|
53
|
+
>
|
|
54
|
+
<Icon /> {e(v)}
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
57
|
+
})}
|
|
58
|
+
</div>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
@@ -244,3 +244,67 @@
|
|
|
244
244
|
max-height 300px
|
|
245
245
|
overflow-y auto
|
|
246
246
|
|
|
247
|
+
// Layout and Workspace dropdown styles
|
|
248
|
+
.layout-workspace-dropdown
|
|
249
|
+
background var(--main)
|
|
250
|
+
border-radius 4px
|
|
251
|
+
box-shadow 0 2px 8px rgba(0, 0, 0, 0.15)
|
|
252
|
+
min-width 200px
|
|
253
|
+
padding 8px
|
|
254
|
+
.ant-tabs-nav
|
|
255
|
+
margin-bottom 8px
|
|
256
|
+
|
|
257
|
+
.layout-menu-content
|
|
258
|
+
max-height 300px
|
|
259
|
+
overflow-y auto
|
|
260
|
+
|
|
261
|
+
.layout-menu-item
|
|
262
|
+
padding 6px 12px
|
|
263
|
+
cursor pointer
|
|
264
|
+
border-radius 4px
|
|
265
|
+
display flex
|
|
266
|
+
align-items center
|
|
267
|
+
gap 8px
|
|
268
|
+
color var(--text)
|
|
269
|
+
&:hover
|
|
270
|
+
background var(--main-dark)
|
|
271
|
+
&.active
|
|
272
|
+
background var(--primary)
|
|
273
|
+
color #fff
|
|
274
|
+
|
|
275
|
+
.workspace-menu-content
|
|
276
|
+
max-height 300px
|
|
277
|
+
overflow-y auto
|
|
278
|
+
|
|
279
|
+
.workspace-save-btn
|
|
280
|
+
margin-bottom 8px
|
|
281
|
+
|
|
282
|
+
.workspace-list
|
|
283
|
+
display flex
|
|
284
|
+
flex-direction column
|
|
285
|
+
gap 4px
|
|
286
|
+
|
|
287
|
+
.workspace-item
|
|
288
|
+
padding 6px 12px
|
|
289
|
+
cursor pointer
|
|
290
|
+
border-radius 4px
|
|
291
|
+
display flex
|
|
292
|
+
align-items center
|
|
293
|
+
justify-content space-between
|
|
294
|
+
color var(--text)
|
|
295
|
+
&:hover
|
|
296
|
+
background var(--main-dark)
|
|
297
|
+
.workspace-delete-icon
|
|
298
|
+
opacity 0
|
|
299
|
+
color var(--text-dark)
|
|
300
|
+
&:hover
|
|
301
|
+
color var(--error)
|
|
302
|
+
&:hover .workspace-delete-icon
|
|
303
|
+
opacity 1
|
|
304
|
+
|
|
305
|
+
.workspace-name
|
|
306
|
+
flex 1
|
|
307
|
+
overflow hidden
|
|
308
|
+
text-overflow ellipsis
|
|
309
|
+
white-space nowrap
|
|
310
|
+
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace save modal component - standalone modal
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React, { useState } from 'react'
|
|
6
|
+
import { auto } from 'manate/react'
|
|
7
|
+
import Modal from '../common/modal'
|
|
8
|
+
import { Input, Select, Button, Space, message, Radio } from 'antd'
|
|
9
|
+
import { SaveOutlined, EditOutlined } from '@ant-design/icons'
|
|
10
|
+
|
|
11
|
+
const e = window.translate
|
|
12
|
+
|
|
13
|
+
export default auto(function WorkspaceSaveModal ({ store }) {
|
|
14
|
+
const { workspaceSaveModalVisible, workspaces } = store
|
|
15
|
+
const [name, setName] = useState('')
|
|
16
|
+
const [selectedId, setSelectedId] = useState(null)
|
|
17
|
+
const [saveMode, setSaveMode] = useState('new') // 'new' or 'overwrite'
|
|
18
|
+
|
|
19
|
+
if (!workspaceSaveModalVisible) {
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function handleClose () {
|
|
24
|
+
window.store.workspaceSaveModalVisible = false
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function handleSave () {
|
|
28
|
+
if (saveMode === 'new') {
|
|
29
|
+
if (!name.trim()) {
|
|
30
|
+
message.error(e('name needed'))
|
|
31
|
+
return
|
|
32
|
+
}
|
|
33
|
+
window.store.saveWorkspace(name.trim())
|
|
34
|
+
message.success(e('saved'))
|
|
35
|
+
} else {
|
|
36
|
+
if (!selectedId) {
|
|
37
|
+
message.error('please Select Workspace')
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
const ws = workspaces.find(w => w.id === selectedId)
|
|
41
|
+
window.store.saveWorkspace(ws?.name || name, selectedId)
|
|
42
|
+
message.success(e('saved'))
|
|
43
|
+
}
|
|
44
|
+
setName('')
|
|
45
|
+
setSelectedId(null)
|
|
46
|
+
setSaveMode('new')
|
|
47
|
+
handleClose()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function handleCancel () {
|
|
51
|
+
setName('')
|
|
52
|
+
setSelectedId(null)
|
|
53
|
+
setSaveMode('new')
|
|
54
|
+
handleClose()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const options = workspaces.map(w => ({
|
|
58
|
+
label: w.name,
|
|
59
|
+
value: w.id
|
|
60
|
+
}))
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<Modal
|
|
64
|
+
title={e('save')}
|
|
65
|
+
open={workspaceSaveModalVisible}
|
|
66
|
+
onCancel={handleCancel}
|
|
67
|
+
footer={null}
|
|
68
|
+
width={400}
|
|
69
|
+
>
|
|
70
|
+
<div className='pd1y'>
|
|
71
|
+
<Space direction='vertical' block>
|
|
72
|
+
<Radio.Group
|
|
73
|
+
value={saveMode}
|
|
74
|
+
onChange={ev => setSaveMode(ev.target.value)}
|
|
75
|
+
>
|
|
76
|
+
<Radio value='new'>
|
|
77
|
+
<SaveOutlined className='mg1r' />
|
|
78
|
+
{e('saveAsNew')}
|
|
79
|
+
</Radio>
|
|
80
|
+
<Radio value='overwrite' disabled={!workspaces.length}>
|
|
81
|
+
<EditOutlined className='mg1r' />
|
|
82
|
+
{e('overwrite')}
|
|
83
|
+
</Radio>
|
|
84
|
+
</Radio.Group>
|
|
85
|
+
|
|
86
|
+
{saveMode === 'new'
|
|
87
|
+
? (
|
|
88
|
+
<Input
|
|
89
|
+
placeholder={e('name')}
|
|
90
|
+
value={name}
|
|
91
|
+
onChange={e => setName(e.target.value)}
|
|
92
|
+
onPressEnter={handleSave}
|
|
93
|
+
/>
|
|
94
|
+
)
|
|
95
|
+
: (
|
|
96
|
+
<Select
|
|
97
|
+
placeholder={e('workspaces')}
|
|
98
|
+
value={selectedId}
|
|
99
|
+
onChange={setSelectedId}
|
|
100
|
+
options={options}
|
|
101
|
+
style={{ width: '100%' }}
|
|
102
|
+
/>
|
|
103
|
+
)}
|
|
104
|
+
|
|
105
|
+
<div className='pd1t'>
|
|
106
|
+
<Button type='primary' onClick={handleSave}>
|
|
107
|
+
{e('save')}
|
|
108
|
+
</Button>
|
|
109
|
+
<Button className='mg1l' onClick={handleCancel}>
|
|
110
|
+
{e('cancel')}
|
|
111
|
+
</Button>
|
|
112
|
+
</div>
|
|
113
|
+
</Space>
|
|
114
|
+
</div>
|
|
115
|
+
</Modal>
|
|
116
|
+
)
|
|
117
|
+
})
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace select content component
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react'
|
|
6
|
+
import { Button, Empty, Popconfirm } from 'antd'
|
|
7
|
+
import {
|
|
8
|
+
SaveOutlined,
|
|
9
|
+
DeleteOutlined
|
|
10
|
+
} from '@ant-design/icons'
|
|
11
|
+
import { auto } from 'manate/react'
|
|
12
|
+
|
|
13
|
+
const e = window.translate
|
|
14
|
+
|
|
15
|
+
export default auto(function WorkspaceSelect (props) {
|
|
16
|
+
const { store } = props
|
|
17
|
+
const { workspaces } = store
|
|
18
|
+
|
|
19
|
+
function handleLoadWorkspace (id) {
|
|
20
|
+
window.store.loadWorkspace(id)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function handleDeleteWorkspace (id, ev) {
|
|
24
|
+
ev.stopPropagation()
|
|
25
|
+
window.store.deleteWorkspace(id)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function handleSaveClick () {
|
|
29
|
+
window.store.workspaceSaveModalVisible = true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className='workspace-menu-content'>
|
|
34
|
+
<div className='workspace-save-btn pd1b'>
|
|
35
|
+
<Button
|
|
36
|
+
type='primary'
|
|
37
|
+
icon={<SaveOutlined />}
|
|
38
|
+
size='small'
|
|
39
|
+
onClick={handleSaveClick}
|
|
40
|
+
block
|
|
41
|
+
>
|
|
42
|
+
{e('save')}
|
|
43
|
+
</Button>
|
|
44
|
+
</div>
|
|
45
|
+
{workspaces.length === 0
|
|
46
|
+
? (
|
|
47
|
+
<Empty
|
|
48
|
+
image={Empty.PRESENTED_IMAGE_SIMPLE}
|
|
49
|
+
description='No items'
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
: (
|
|
53
|
+
<div className='workspace-list'>
|
|
54
|
+
{workspaces.map(ws => (
|
|
55
|
+
<div
|
|
56
|
+
key={ws.id}
|
|
57
|
+
className='workspace-item'
|
|
58
|
+
onClick={() => handleLoadWorkspace(ws.id)}
|
|
59
|
+
>
|
|
60
|
+
<span className='workspace-name'>{ws.name}</span>
|
|
61
|
+
<Popconfirm
|
|
62
|
+
title={e('del') + '?'}
|
|
63
|
+
onConfirm={(ev) => handleDeleteWorkspace(ws.id, ev)}
|
|
64
|
+
onCancel={(ev) => ev.stopPropagation()}
|
|
65
|
+
okText={e('ok')}
|
|
66
|
+
cancelText={e('cancel')}
|
|
67
|
+
>
|
|
68
|
+
<DeleteOutlined
|
|
69
|
+
className='workspace-delete-icon'
|
|
70
|
+
onClick={(ev) => ev.stopPropagation()}
|
|
71
|
+
/>
|
|
72
|
+
</Popconfirm>
|
|
73
|
+
</div>
|
|
74
|
+
))}
|
|
75
|
+
</div>
|
|
76
|
+
)}
|
|
77
|
+
</div>
|
|
78
|
+
)
|
|
79
|
+
})
|
|
@@ -34,7 +34,13 @@ export default class AttachAddonCustom extends AttachAddon {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
onMsg = (ev) => {
|
|
37
|
-
|
|
37
|
+
// When in alternate screen mode (like vim, less, or TUI apps like Claude Code),
|
|
38
|
+
// bypass trzsz processing to avoid interference with the application's display
|
|
39
|
+
if (this.term?.buffer?.active?.type === 'alternate') {
|
|
40
|
+
this.writeToTerminal(ev.data)
|
|
41
|
+
} else {
|
|
42
|
+
this.trzsz.processServerOutput(ev.data)
|
|
43
|
+
}
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
writeToTerminal = (data) => {
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { useEffect, useState } from 'react'
|
|
6
|
-
import {
|
|
6
|
+
import { Form, Button } from 'antd'
|
|
7
|
+
import Modal from '../common/modal'
|
|
7
8
|
import InputAutoFocus from '../common/input-auto-focus'
|
|
8
9
|
import wait from '../../common/wait'
|
|
9
10
|
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import { PureComponent } from 'react'
|
|
6
6
|
import TextEditorForm from './text-editor-form'
|
|
7
|
-
import { Spin
|
|
7
|
+
import { Spin } from 'antd'
|
|
8
|
+
import Modal from '../common/modal'
|
|
8
9
|
import resolve from '../../common/resolve'
|
|
9
10
|
import { refsStatic, refs } from '../common/ref'
|
|
10
11
|
|