@electerm/electerm-react 1.51.21 → 1.60.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 +2 -0
- package/client/common/default-setting.js +12 -1
- package/client/components/ai/ai-chat-history-item.jsx +69 -0
- package/client/components/ai/ai-chat-history.jsx +31 -0
- package/client/components/ai/ai-chat.jsx +172 -0
- package/client/components/ai/ai-config.jsx +145 -0
- package/client/components/ai/ai-output.jsx +118 -0
- package/client/components/ai/ai.styl +70 -0
- package/client/components/ai/get-brand.js +34 -0
- package/client/components/ai/providers.js +14 -0
- package/client/components/bookmark-form/rdp-form-ui.jsx +1 -1
- package/client/components/footer/batch-input.jsx +13 -67
- package/client/components/footer/footer-entry.jsx +19 -3
- package/client/components/footer/footer.styl +4 -0
- package/client/components/footer/tab-select.jsx +9 -3
- package/client/components/layout/layout.jsx +5 -4
- package/client/components/main/main.jsx +20 -4
- package/client/components/shortcuts/shortcut-control.jsx +17 -2
- package/client/components/shortcuts/shortcut-handler.js +24 -8
- package/client/components/shortcuts/shortcuts-defaults.js +6 -0
- package/client/components/side-panel-r/right-side-panel.styl +6 -7
- package/client/components/side-panel-r/side-panel-r.jsx +32 -10
- package/client/components/sidebar/app-running-time.jsx +35 -0
- package/client/components/sidebar/history-item.jsx +20 -3
- package/client/components/sidebar/history.jsx +4 -1
- package/client/components/sidebar/info-modal.jsx +2 -0
- package/client/components/tabs/app-drag.jsx +1 -1
- package/client/components/tabs/index.jsx +16 -43
- package/client/components/tabs/no-session.jsx +40 -0
- package/client/components/tabs/tabs.styl +6 -0
- package/client/components/terminal/index.jsx +2 -2
- package/client/store/common.js +37 -2
- package/client/store/index.js +2 -290
- package/client/store/init-state.js +7 -1
- package/client/store/store.js +313 -0
- package/client/store/sync.js +4 -1
- package/client/store/tab.js +56 -2
- package/client/store/watch.js +9 -2
- package/package.json +1 -1
|
@@ -26,37 +26,23 @@ export default class BatchInput extends Component {
|
|
|
26
26
|
super(props)
|
|
27
27
|
this.state = {
|
|
28
28
|
cmd: '',
|
|
29
|
-
selectedTabIds: [props.activeTabId],
|
|
30
29
|
open: false,
|
|
31
30
|
enter: false
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
componentDidUpdate (prevProps) {
|
|
36
|
-
if (prevProps.activeTabId !== this.props.activeTabId) {
|
|
37
|
-
this.setState(prevState => {
|
|
38
|
-
const newSelectedTabIds = prevState.selectedTabIds.filter(
|
|
39
|
-
id => id !== this.props.activeTabId
|
|
40
|
-
)
|
|
41
|
-
newSelectedTabIds.unshift(this.props.activeTabId)
|
|
42
|
-
return {
|
|
43
|
-
selectedTabIds: newSelectedTabIds
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
34
|
componentWillUnmount () {
|
|
50
35
|
clearTimeout(this.timer)
|
|
51
36
|
}
|
|
52
37
|
|
|
53
38
|
handleEnter = (e) => {
|
|
54
|
-
const {
|
|
39
|
+
const { batchInputSelectedTabIds } = window.store
|
|
40
|
+
const { cmd } = this.state
|
|
55
41
|
if (!cmd.trim()) {
|
|
56
42
|
return
|
|
57
43
|
}
|
|
58
44
|
window.store.addBatchInput(cmd)
|
|
59
|
-
this.props.input(cmd,
|
|
45
|
+
this.props.input(cmd, Array.from(batchInputSelectedTabIds))
|
|
60
46
|
this.setState({
|
|
61
47
|
cmd: '',
|
|
62
48
|
open: false
|
|
@@ -64,43 +50,6 @@ export default class BatchInput extends Component {
|
|
|
64
50
|
e.stopPropagation()
|
|
65
51
|
}
|
|
66
52
|
|
|
67
|
-
onSelectAll = () => {
|
|
68
|
-
this.setState({
|
|
69
|
-
selectedTabIds: this.getTabs().map(tab => tab.id)
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
onSelectNone = () => {
|
|
74
|
-
this.setState({
|
|
75
|
-
selectedTabIds: [this.props.activeTabId]
|
|
76
|
-
})
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
filterValidTabIds = (tabIds) => {
|
|
80
|
-
return tabIds.filter(id => {
|
|
81
|
-
return this.props.tabs.some(tab => tab.id === id)
|
|
82
|
-
})
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
onSelect = (id) => {
|
|
86
|
-
this.setState(prevState => {
|
|
87
|
-
const selectedTabIds = prevState.selectedTabIds.includes(id)
|
|
88
|
-
? prevState.selectedTabIds.filter(tabId => tabId !== id)
|
|
89
|
-
: [...prevState.selectedTabIds, id]
|
|
90
|
-
|
|
91
|
-
// Ensure at least the current tab is selected
|
|
92
|
-
if (selectedTabIds.length === 0) {
|
|
93
|
-
return {
|
|
94
|
-
selectedTabIds: [this.props.activeTabId]
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return {
|
|
99
|
-
selectedTabIds
|
|
100
|
-
}
|
|
101
|
-
})
|
|
102
|
-
}
|
|
103
|
-
|
|
104
53
|
handleChange = (v = '') => {
|
|
105
54
|
let vv = v.replace(/^\d+:/, '').replace(/\n$/, '')
|
|
106
55
|
if (vv === batchInputLsKey) {
|
|
@@ -118,15 +67,9 @@ export default class BatchInput extends Component {
|
|
|
118
67
|
|
|
119
68
|
handleClick = () => {
|
|
120
69
|
this.setState({
|
|
121
|
-
open: true
|
|
122
|
-
selectedTabIds: this.filterValidTabIds(this.state.selectedTabIds)
|
|
123
|
-
})
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
handleChangeAll = toAll => {
|
|
127
|
-
this.setState({
|
|
128
|
-
toAll
|
|
70
|
+
open: true
|
|
129
71
|
})
|
|
72
|
+
window.store.filterBatchInputSelectedTabIds()
|
|
130
73
|
}
|
|
131
74
|
|
|
132
75
|
handleBlur = () => {
|
|
@@ -192,7 +135,10 @@ export default class BatchInput extends Component {
|
|
|
192
135
|
}
|
|
193
136
|
|
|
194
137
|
render () {
|
|
195
|
-
const { cmd, open,
|
|
138
|
+
const { cmd, open, enter } = this.state
|
|
139
|
+
const {
|
|
140
|
+
batchInputSelectedTabIds
|
|
141
|
+
} = this.props
|
|
196
142
|
const opts = {
|
|
197
143
|
options: this.buildOptions(),
|
|
198
144
|
placeholder: e('batchInput'),
|
|
@@ -217,10 +163,10 @@ export default class BatchInput extends Component {
|
|
|
217
163
|
const tabSelectProps = {
|
|
218
164
|
activeTabId: this.props.activeTabId,
|
|
219
165
|
tabs: this.getTabs(),
|
|
220
|
-
selectedTabIds,
|
|
221
|
-
onSelectAll:
|
|
222
|
-
onSelectNone:
|
|
223
|
-
onSelect:
|
|
166
|
+
selectedTabIds: batchInputSelectedTabIds,
|
|
167
|
+
onSelectAll: window.store.selectAllBatchInputTabs,
|
|
168
|
+
onSelectNone: window.store.selectNoneBatchInputTabs,
|
|
169
|
+
onSelect: window.store.onSelectBatchInputSelectedTabId
|
|
224
170
|
}
|
|
225
171
|
return (
|
|
226
172
|
<span
|
|
@@ -49,11 +49,13 @@ export default auto(function FooterEntry (props) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function renderBatchInputs () {
|
|
52
|
+
const { store } = props
|
|
52
53
|
const batchProps = {
|
|
53
54
|
input: batchInput,
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
tabs: store.tabs,
|
|
56
|
+
batchInputs: store.batchInputs,
|
|
57
|
+
batchInputSelectedTabIds: store.batchInputSelectedTabIds,
|
|
58
|
+
activeTabId: store.activeTabId
|
|
57
59
|
}
|
|
58
60
|
return (
|
|
59
61
|
<div className='terminal-footer-unit terminal-footer-center'>
|
|
@@ -72,6 +74,19 @@ export default auto(function FooterEntry (props) {
|
|
|
72
74
|
)
|
|
73
75
|
}
|
|
74
76
|
|
|
77
|
+
function renderAIIcon () {
|
|
78
|
+
return (
|
|
79
|
+
<div className='terminal-footer-unit terminal-footer-ai'>
|
|
80
|
+
<span
|
|
81
|
+
className='ai-icon'
|
|
82
|
+
onClick={window.store.handleOpenAIPanel}
|
|
83
|
+
>
|
|
84
|
+
AI
|
|
85
|
+
</span>
|
|
86
|
+
</div>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
75
90
|
function renderEncodingInfo () {
|
|
76
91
|
const selectProps = {
|
|
77
92
|
style: {
|
|
@@ -148,6 +163,7 @@ export default auto(function FooterEntry (props) {
|
|
|
148
163
|
return (
|
|
149
164
|
<div {...sideProps}>
|
|
150
165
|
<div className='terminal-footer-flex'>
|
|
166
|
+
{renderAIIcon()}
|
|
151
167
|
{renderQuickCommands()}
|
|
152
168
|
{renderBatchInputs()}
|
|
153
169
|
{renderEncodingInfo()}
|
|
@@ -14,7 +14,7 @@ export default function TabSelect (props) {
|
|
|
14
14
|
const itemProps = {
|
|
15
15
|
tab,
|
|
16
16
|
selected,
|
|
17
|
-
onSelect:
|
|
17
|
+
onSelect: window.store.onSelectBatchInputSelectedTabId,
|
|
18
18
|
id: tab.id,
|
|
19
19
|
isCurrent: tab.id === activeTabId
|
|
20
20
|
}
|
|
@@ -26,18 +26,24 @@ export default function TabSelect (props) {
|
|
|
26
26
|
)
|
|
27
27
|
})
|
|
28
28
|
}
|
|
29
|
+
function onSelectAll () {
|
|
30
|
+
window.store.selectAllBatchInputTabs()
|
|
31
|
+
}
|
|
32
|
+
function onSelectNone () {
|
|
33
|
+
window.store.selectNoneBatchInputTabs()
|
|
34
|
+
}
|
|
29
35
|
function renderBtns () {
|
|
30
36
|
return (
|
|
31
37
|
<div className='pd1t pd2b font12'>
|
|
32
38
|
<span
|
|
33
39
|
className='mg1r pointer'
|
|
34
|
-
onClick={
|
|
40
|
+
onClick={onSelectAll}
|
|
35
41
|
>
|
|
36
42
|
All
|
|
37
43
|
</span>
|
|
38
44
|
<span
|
|
39
45
|
className='pointer'
|
|
40
|
-
onClick={
|
|
46
|
+
onClick={onSelectNone}
|
|
41
47
|
>
|
|
42
48
|
None
|
|
43
49
|
</span>
|
|
@@ -61,12 +61,13 @@ export default auto(function Layout (props) {
|
|
|
61
61
|
width,
|
|
62
62
|
pinnedQuickCommandBar,
|
|
63
63
|
leftSidebarWidth,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
rightPanelVisible,
|
|
65
|
+
rightPanelPinned,
|
|
66
|
+
rightPanelWidth,
|
|
67
|
+
pinned
|
|
67
68
|
} = props.store
|
|
68
69
|
const l = pinned ? leftSidebarWidth : 0
|
|
69
|
-
const r =
|
|
70
|
+
const r = rightPanelPinned && rightPanelVisible ? rightPanelWidth : 0
|
|
70
71
|
const w = width - l - r - 42
|
|
71
72
|
const h = height - footerHeight - (pinnedQuickCommandBar ? quickCommandBoxHeight : 0)
|
|
72
73
|
return layoutAlg(layout, w, h)
|
|
@@ -29,6 +29,7 @@ import RightSidePanel from '../side-panel-r/side-panel-r'
|
|
|
29
29
|
import ConnectionHoppingWarning from './connection-hopping-warnning'
|
|
30
30
|
import SshConfigLoadNotify from '../ssh-config/ssh-config-load-notify'
|
|
31
31
|
import LoadSshConfigs from '../ssh-config/load-ssh-configs'
|
|
32
|
+
import AIChat from '../ai/ai-chat'
|
|
32
33
|
import { pick } from 'lodash-es'
|
|
33
34
|
import deepCopy from 'json-deep-copy'
|
|
34
35
|
import './wrapper.styl'
|
|
@@ -81,7 +82,7 @@ export default auto(function Index (props) {
|
|
|
81
82
|
store.isSecondInstance = window.pre.runSync('isSecondInstance')
|
|
82
83
|
store.initData()
|
|
83
84
|
store.checkForDbUpgrade()
|
|
84
|
-
window.pre.runGlobalAsync('registerDeepLink')
|
|
85
|
+
// window.pre.runGlobalAsync('registerDeepLink')
|
|
85
86
|
}, [])
|
|
86
87
|
|
|
87
88
|
const { store } = props
|
|
@@ -98,7 +99,9 @@ export default auto(function Index (props) {
|
|
|
98
99
|
uiThemeConfig,
|
|
99
100
|
transferHistory,
|
|
100
101
|
transferToConfirm,
|
|
101
|
-
openResolutionEdit
|
|
102
|
+
openResolutionEdit,
|
|
103
|
+
rightPanelTitle,
|
|
104
|
+
rightPanelTab
|
|
102
105
|
} = store
|
|
103
106
|
const upgradeInfo = deepCopy(store.upgradeInfo)
|
|
104
107
|
const cls = classnames({
|
|
@@ -190,7 +193,9 @@ export default auto(function Index (props) {
|
|
|
190
193
|
const rightPanelProps = {
|
|
191
194
|
rightPanelVisible: store.rightPanelVisible,
|
|
192
195
|
rightPanelPinned: store.rightPanelPinned,
|
|
193
|
-
rightPanelWidth: store.rightPanelWidth
|
|
196
|
+
rightPanelWidth: store.rightPanelWidth,
|
|
197
|
+
title: rightPanelTitle,
|
|
198
|
+
rightPanelTab
|
|
194
199
|
}
|
|
195
200
|
const terminalInfoProps = {
|
|
196
201
|
...deepCopy(store.terminalInfoProps),
|
|
@@ -213,6 +218,17 @@ export default auto(function Index (props) {
|
|
|
213
218
|
hasOldConnectionHoppingBookmark: store.hasOldConnectionHoppingBookmark,
|
|
214
219
|
configLoaded
|
|
215
220
|
}
|
|
221
|
+
const aiChatProps = {
|
|
222
|
+
aiChatHistory: store.aiChatHistory,
|
|
223
|
+
config,
|
|
224
|
+
selectedTabIds: store.batchInputSelectedTabIds,
|
|
225
|
+
tabs: store.getTabs(),
|
|
226
|
+
activeTabId: store.activeTabId,
|
|
227
|
+
showAIConfig: store.showAIConfig
|
|
228
|
+
}
|
|
229
|
+
const rightPanelContent = rightPanelTab === 'ai'
|
|
230
|
+
? <AIChat {...aiChatProps} />
|
|
231
|
+
: <TerminalInfo {...terminalInfoProps} />
|
|
216
232
|
return (
|
|
217
233
|
<ConfigProvider
|
|
218
234
|
theme={uiThemeConfig}
|
|
@@ -269,7 +285,7 @@ export default auto(function Index (props) {
|
|
|
269
285
|
<Resolutions {...resProps} />
|
|
270
286
|
<InfoModal {...infoModalProps} />
|
|
271
287
|
<RightSidePanel {...rightPanelProps}>
|
|
272
|
-
|
|
288
|
+
{rightPanelContent}
|
|
273
289
|
</RightSidePanel>
|
|
274
290
|
<SshConfigLoadNotify {...sshConfigProps} />
|
|
275
291
|
<LoadSshConfigs
|
|
@@ -9,8 +9,10 @@ import { throttle } from 'lodash-es'
|
|
|
9
9
|
|
|
10
10
|
class ShortcutControl extends React.PureComponent {
|
|
11
11
|
componentDidMount () {
|
|
12
|
-
|
|
13
|
-
window.addEventListener('
|
|
12
|
+
const onEvent = this.handleKeyboardEvent.bind(this)
|
|
13
|
+
window.addEventListener('keydown', onEvent)
|
|
14
|
+
window.addEventListener('mousedown', onEvent)
|
|
15
|
+
window.addEventListener('mousewheel', onEvent)
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
closeCurrentTabShortcut = throttle((e) => {
|
|
@@ -21,6 +23,19 @@ class ShortcutControl extends React.PureComponent {
|
|
|
21
23
|
}
|
|
22
24
|
}, 500)
|
|
23
25
|
|
|
26
|
+
mouseWheelDownCloseTabShortcut = throttle((e) => {
|
|
27
|
+
e.stopPropagation()
|
|
28
|
+
|
|
29
|
+
// Check if the event target is within a .tab element
|
|
30
|
+
const tabElement = e.target.closest('.tab')
|
|
31
|
+
if (tabElement) {
|
|
32
|
+
const tabId = tabElement.getAttribute('data-id')
|
|
33
|
+
if (tabId) {
|
|
34
|
+
window.store.delTab(tabId)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}, 500)
|
|
38
|
+
|
|
24
39
|
reloadCurrentTabShortcut = throttle((e) => {
|
|
25
40
|
e.stopPropagation()
|
|
26
41
|
window.store.reloadTab()
|
|
@@ -53,12 +53,14 @@ export function shortcutExtend (Cls) {
|
|
|
53
53
|
metaKey,
|
|
54
54
|
altKey,
|
|
55
55
|
wheelDeltaY,
|
|
56
|
+
button,
|
|
56
57
|
type,
|
|
57
58
|
key
|
|
58
59
|
} = event
|
|
59
60
|
if (this.cmdAddon) {
|
|
60
61
|
this.cmdAddon.handleKey(event)
|
|
61
62
|
}
|
|
63
|
+
|
|
62
64
|
if (
|
|
63
65
|
this.term &&
|
|
64
66
|
key === 'Backspace' &&
|
|
@@ -66,6 +68,7 @@ export function shortcutExtend (Cls) {
|
|
|
66
68
|
!altKey &&
|
|
67
69
|
!ctrlKey
|
|
68
70
|
) {
|
|
71
|
+
console.log('handleKeyboardEvent: Handling Backspace key')
|
|
69
72
|
this.props.onDelKeyPressed()
|
|
70
73
|
const delKey = this.props.config.backspaceMode === '^?' ? 8 : 127
|
|
71
74
|
const altDelDelKey = delKey === 8 ? 127 : 8
|
|
@@ -78,6 +81,7 @@ export function shortcutExtend (Cls) {
|
|
|
78
81
|
) {
|
|
79
82
|
return true
|
|
80
83
|
}
|
|
84
|
+
|
|
81
85
|
if (
|
|
82
86
|
this.term &&
|
|
83
87
|
key === 'c' &&
|
|
@@ -89,31 +93,42 @@ export function shortcutExtend (Cls) {
|
|
|
89
93
|
) {
|
|
90
94
|
this.onZmodemEnd()
|
|
91
95
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
|
|
97
|
+
let codeName
|
|
98
|
+
if (type === 'mousedown' && button === 1) {
|
|
99
|
+
codeName = 'mouseWheel'
|
|
100
|
+
} else {
|
|
101
|
+
codeName = event instanceof window.WheelEvent
|
|
102
|
+
? (wheelDeltaY > 0 ? 'mouseWheelUp' : 'mouseWheelDown')
|
|
103
|
+
: code
|
|
104
|
+
}
|
|
105
|
+
|
|
95
106
|
const codeK = getKeyCharacter(codeName)
|
|
96
|
-
|
|
107
|
+
|
|
108
|
+
const noControlKey = type !== 'mousedown' && !ctrlKey && !shiftKey && !metaKey && !altKey
|
|
97
109
|
if (noControlKey) {
|
|
98
110
|
return
|
|
99
111
|
}
|
|
100
|
-
const r =
|
|
112
|
+
const r = codeName === 'mouseWheel'
|
|
113
|
+
? 'mouseWheel'
|
|
114
|
+
: (ctrlKey ? 'ctrl+' : '') +
|
|
101
115
|
(metaKey ? 'meta+' : '') +
|
|
102
116
|
(shiftKey ? 'shift+' : '') +
|
|
103
117
|
(altKey ? 'alt+' : '') +
|
|
104
118
|
codeK.toLowerCase()
|
|
119
|
+
|
|
105
120
|
const shortcutsConfig = buildConfig(this.props.config, d => !d.hidden)
|
|
106
121
|
const keys = Object.keys(shortcutsConfig)
|
|
107
122
|
const len = keys.length
|
|
123
|
+
|
|
108
124
|
if (this.term) {
|
|
109
125
|
const qmMatch = window.store.quickCommands.find(d => d.shortcut === r)
|
|
110
126
|
if (qmMatch) {
|
|
111
|
-
window.store.runQuickCommandItem(
|
|
112
|
-
qmMatch.id
|
|
113
|
-
)
|
|
127
|
+
window.store.runQuickCommandItem(qmMatch.id)
|
|
114
128
|
return false
|
|
115
129
|
}
|
|
116
130
|
}
|
|
131
|
+
|
|
117
132
|
for (let i = 0; i < len; i++) {
|
|
118
133
|
const k = keys[i]
|
|
119
134
|
const conf = shortcutsConfig[k]
|
|
@@ -128,6 +143,7 @@ export function shortcutExtend (Cls) {
|
|
|
128
143
|
}
|
|
129
144
|
}
|
|
130
145
|
}
|
|
146
|
+
|
|
131
147
|
return !!this.term
|
|
132
148
|
}
|
|
133
149
|
return Cls
|
|
@@ -3,29 +3,28 @@
|
|
|
3
3
|
.right-side-panel
|
|
4
4
|
position absolute
|
|
5
5
|
right 0
|
|
6
|
-
top
|
|
6
|
+
top 36px
|
|
7
7
|
bottom 0
|
|
8
8
|
z-index 200
|
|
9
9
|
background main
|
|
10
10
|
color text-light
|
|
11
11
|
border-left 1px solid darken(main, 30%)
|
|
12
|
+
&.right-side-panel-pinned
|
|
13
|
+
top 0
|
|
12
14
|
|
|
13
15
|
.drag-handle
|
|
14
16
|
left 0
|
|
15
17
|
right auto
|
|
16
18
|
display block
|
|
17
|
-
|
|
19
|
+
.right-panel-title
|
|
20
|
+
border-bottom 1px solid darken(main, 30%)
|
|
18
21
|
.right-side-panel-controls
|
|
19
|
-
position absolute
|
|
20
|
-
left 10px
|
|
21
|
-
top 10px
|
|
22
22
|
color text-dark
|
|
23
23
|
font-size 16px
|
|
24
24
|
&:hover
|
|
25
25
|
color text
|
|
26
26
|
cursor pointer
|
|
27
27
|
&.right-side-panel-pin
|
|
28
|
-
left 30px
|
|
29
28
|
&.pinned
|
|
30
29
|
color success
|
|
31
30
|
.right-side-panel-content
|
|
@@ -33,7 +32,7 @@
|
|
|
33
32
|
overflow-y scroll
|
|
34
33
|
position absolute
|
|
35
34
|
left 0
|
|
36
|
-
top
|
|
35
|
+
top 55px
|
|
37
36
|
right 0
|
|
38
37
|
bottom 0
|
|
39
38
|
.animate-fast
|
|
@@ -3,20 +3,31 @@ import DragHandle from '../common/drag-handle'
|
|
|
3
3
|
import './right-side-panel.styl'
|
|
4
4
|
import {
|
|
5
5
|
CloseCircleOutlined,
|
|
6
|
-
PushpinOutlined
|
|
6
|
+
PushpinOutlined,
|
|
7
|
+
InfoCircleOutlined
|
|
7
8
|
} from '@ant-design/icons'
|
|
9
|
+
import {
|
|
10
|
+
Typography,
|
|
11
|
+
Flex,
|
|
12
|
+
Tag
|
|
13
|
+
} from 'antd'
|
|
8
14
|
|
|
9
15
|
export default memo(function RightSidePanel (
|
|
10
16
|
{
|
|
11
17
|
rightPanelVisible,
|
|
12
18
|
rightPanelPinned,
|
|
13
19
|
rightPanelWidth,
|
|
14
|
-
children
|
|
20
|
+
children,
|
|
21
|
+
title,
|
|
22
|
+
rightPanelTab
|
|
15
23
|
}
|
|
16
24
|
) {
|
|
17
25
|
if (!rightPanelVisible) {
|
|
18
26
|
return null
|
|
19
27
|
}
|
|
28
|
+
const tag = rightPanelTab === 'ai'
|
|
29
|
+
? <Tag className='mg1r'>AI</Tag>
|
|
30
|
+
: <InfoCircleOutlined className='mg1r' />
|
|
20
31
|
|
|
21
32
|
function onDragEnd (nw) {
|
|
22
33
|
window.store.setRightSidePanelWidth(nw)
|
|
@@ -36,7 +47,7 @@ export default memo(function RightSidePanel (
|
|
|
36
47
|
}
|
|
37
48
|
|
|
38
49
|
const panelProps = {
|
|
39
|
-
className: 'right-side-panel animate-fast',
|
|
50
|
+
className: 'right-side-panel animate-fast' + (rightPanelPinned ? ' right-side-panel-pinned' : ''),
|
|
40
51
|
id: 'right-side-panel',
|
|
41
52
|
style: {
|
|
42
53
|
width: `${rightPanelWidth}px`
|
|
@@ -60,13 +71,24 @@ export default memo(function RightSidePanel (
|
|
|
60
71
|
{...panelProps}
|
|
61
72
|
>
|
|
62
73
|
<DragHandle {...dragProps} />
|
|
63
|
-
<
|
|
64
|
-
className='right-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
{
|
|
69
|
-
|
|
74
|
+
<Flex
|
|
75
|
+
className='right-panel-title pd2'
|
|
76
|
+
justify='space-between'
|
|
77
|
+
align='center'
|
|
78
|
+
>
|
|
79
|
+
<Typography.Text level={4} ellipsis style={{ margin: 0, flex: 1 }}>
|
|
80
|
+
{tag} {title}
|
|
81
|
+
</Typography.Text>
|
|
82
|
+
<Flex>
|
|
83
|
+
<PushpinOutlined
|
|
84
|
+
{...pinProps}
|
|
85
|
+
/>
|
|
86
|
+
<CloseCircleOutlined
|
|
87
|
+
className='right-side-panel-close right-side-panel-controls mg1l'
|
|
88
|
+
onClick={onClose}
|
|
89
|
+
/>
|
|
90
|
+
</Flex>
|
|
91
|
+
</Flex>
|
|
70
92
|
<div className='right-side-panel-content'>
|
|
71
93
|
{children}
|
|
72
94
|
</div>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
ClockCircleOutlined
|
|
4
|
+
} from '@ant-design/icons'
|
|
5
|
+
import dayjs from 'dayjs'
|
|
6
|
+
import duration from 'dayjs/plugin/duration'
|
|
7
|
+
|
|
8
|
+
dayjs.extend(duration)
|
|
9
|
+
|
|
10
|
+
const e = window.translate
|
|
11
|
+
|
|
12
|
+
export default function RunningTime () {
|
|
13
|
+
const [runningTime, setRunningTime] = useState(0)
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const startTime = window.pre.runSync('getInitTime')
|
|
17
|
+
|
|
18
|
+
const timer = setInterval(() => {
|
|
19
|
+
const currentTime = Date.now()
|
|
20
|
+
const diff = Math.floor((currentTime - startTime) / 1000)
|
|
21
|
+
setRunningTime(diff)
|
|
22
|
+
}, 1000)
|
|
23
|
+
|
|
24
|
+
return () => clearInterval(timer)
|
|
25
|
+
}, [])
|
|
26
|
+
const formatRunningTime = (seconds) => {
|
|
27
|
+
return dayjs.duration(seconds, 'seconds').format('HH:mm:ss')
|
|
28
|
+
}
|
|
29
|
+
return (
|
|
30
|
+
<p className='mg2b'>
|
|
31
|
+
<ClockCircleOutlined /> <b>{e('runningTime')} ➾</b>
|
|
32
|
+
<span className='mg1l'>{formatRunningTime(runningTime)}</span>
|
|
33
|
+
</p>
|
|
34
|
+
)
|
|
35
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useRef } from 'react'
|
|
1
2
|
import createTitle, { createTitleWithTag } from '../../common/create-title'
|
|
2
3
|
import { DeleteOutlined } from '@ant-design/icons'
|
|
3
4
|
|
|
@@ -7,9 +8,25 @@ export default function HistoryItem (props) {
|
|
|
7
8
|
item,
|
|
8
9
|
index
|
|
9
10
|
} = props
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
const timeoutRef = useRef(null)
|
|
12
|
+
|
|
13
|
+
const handleClick = useCallback(() => {
|
|
14
|
+
if (timeoutRef.current) {
|
|
15
|
+
clearTimeout(timeoutRef.current)
|
|
16
|
+
}
|
|
17
|
+
timeoutRef.current = setTimeout(() => {
|
|
18
|
+
store.onSelectHistory(item.tab)
|
|
19
|
+
}, 10)
|
|
20
|
+
}, [item.tab])
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
return () => {
|
|
24
|
+
if (timeoutRef.current) {
|
|
25
|
+
clearTimeout(timeoutRef.current)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}, [])
|
|
29
|
+
|
|
13
30
|
function handleDelete (e) {
|
|
14
31
|
e.stopPropagation()
|
|
15
32
|
store.history.splice(index, 1)
|
|
@@ -10,13 +10,16 @@ export default auto(function HistoryPanel (props) {
|
|
|
10
10
|
const {
|
|
11
11
|
history
|
|
12
12
|
} = store
|
|
13
|
+
const arr = props.sort
|
|
14
|
+
? [...history].sort((a, b) => { return b.count - a.count })
|
|
15
|
+
: history
|
|
13
16
|
return (
|
|
14
17
|
<div
|
|
15
18
|
className='sidebar-panel-history'
|
|
16
19
|
>
|
|
17
20
|
<div className='pd2x'>
|
|
18
21
|
{
|
|
19
|
-
|
|
22
|
+
arr.map((item, i) => {
|
|
20
23
|
return (
|
|
21
24
|
<HistoryItem
|
|
22
25
|
key={item.id}
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import { Modal, Tabs, Button } from 'antd'
|
|
16
16
|
import Link from '../common/external-link'
|
|
17
17
|
import LogoElem from '../common/logo-elem'
|
|
18
|
+
import RunningTime from './app-running-time'
|
|
18
19
|
|
|
19
20
|
import {
|
|
20
21
|
packInfo,
|
|
@@ -121,6 +122,7 @@ export default memo(function InfoModal (props) {
|
|
|
121
122
|
<div>
|
|
122
123
|
<LogoElem />
|
|
123
124
|
<p className='mg2b'>{e('desc')}</p>
|
|
125
|
+
<RunningTime />
|
|
124
126
|
<p className='mg1b'>
|
|
125
127
|
<UserOutlined /> <b className='mg1r'>{e('author')} ➾</b>
|
|
126
128
|
<Link to={authorUrl} className='mg1l'>
|