@electerm/electerm-react 3.10.0 → 3.11.0
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/bookmark-schemas.js +164 -0
- package/client/common/ws.js +25 -6
- package/client/common/zod.js +180 -0
- package/client/components/ai/agent-tool-call-card.jsx +90 -0
- package/client/components/ai/agent-tools.js +193 -0
- package/client/components/ai/agent.js +159 -0
- package/client/components/ai/ai-chat-entry.jsx +11 -0
- package/client/components/ai/ai-chat-history-item.jsx +48 -2
- package/client/components/ai/ai-chat.jsx +25 -6
- package/client/components/ai/ai-config.jsx +45 -4
- package/client/components/ai/ai.styl +73 -0
- package/client/components/main/main.jsx +3 -3
- package/client/components/rdp/file-transfer.js +3 -0
- package/client/components/terminal/terminal-error-handle.jsx +1 -1
- package/client/components/terminal/terminal-interactive-ui.jsx +157 -0
- package/client/components/terminal/terminal-interactive.jsx +64 -163
- package/client/components/terminal-info/terminal-info-entry.jsx +11 -0
- package/client/components/text-editor/text-editor-entry.jsx +11 -0
- package/client/components/widgets/widget-form.jsx +27 -2
- package/client/entry/worker.js +9 -5
- package/package.json +1 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { agentTools, executeToolCall } from './agent-tools'
|
|
2
|
+
|
|
3
|
+
const MAX_ITERATIONS = 15
|
|
4
|
+
|
|
5
|
+
function buildAgentSystemPrompt (config) {
|
|
6
|
+
const lang = config.languageAI || window.store.getLangName()
|
|
7
|
+
const baseRole = config.roleAI || 'You are a helpful assistant.'
|
|
8
|
+
return `${baseRole}
|
|
9
|
+
|
|
10
|
+
You are operating inside electerm, a terminal/SSH client. You have access to tools that let you:
|
|
11
|
+
- Run commands in terminal tabs and read their output
|
|
12
|
+
- Open new terminal tabs (local or SSH)
|
|
13
|
+
- Manage bookmarks (create, list, open connections)
|
|
14
|
+
- Switch between tabs
|
|
15
|
+
|
|
16
|
+
When the user asks you to perform terminal operations, use the available tools.
|
|
17
|
+
Always explain what you are doing before executing commands.
|
|
18
|
+
If a command produces errors, analyze the output and try to fix the issue.
|
|
19
|
+
Prefer using the active terminal unless the user specifies otherwise.
|
|
20
|
+
For SSH connections, create a bookmark and open it rather than running ssh directly.
|
|
21
|
+
|
|
22
|
+
Reply in ${lang} language.`
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function updateChatEntry (chatEntry, updates) {
|
|
26
|
+
const index = window.store.aiChatHistory.findIndex(i => i.id === chatEntry.id)
|
|
27
|
+
if (index !== -1) {
|
|
28
|
+
Object.assign(window.store.aiChatHistory[index], updates)
|
|
29
|
+
window.store.aiChatHistory = [...window.store.aiChatHistory]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function callBackendAIchatWithTools (messages, config) {
|
|
34
|
+
return window.pre.runGlobalAsync(
|
|
35
|
+
'AIchatWithTools',
|
|
36
|
+
messages,
|
|
37
|
+
config.modelAI,
|
|
38
|
+
config.baseURLAI,
|
|
39
|
+
config.apiPathAI,
|
|
40
|
+
config.apiKeyAI,
|
|
41
|
+
config.proxyAI,
|
|
42
|
+
agentTools
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function runAgentLoop (chatEntry, config, abortRef) {
|
|
47
|
+
const messages = [
|
|
48
|
+
{ role: 'system', content: buildAgentSystemPrompt(config) },
|
|
49
|
+
{ role: 'user', content: chatEntry.prompt }
|
|
50
|
+
]
|
|
51
|
+
const toolCallsLog = []
|
|
52
|
+
let accumulatedContent = ''
|
|
53
|
+
|
|
54
|
+
updateChatEntry(chatEntry, {
|
|
55
|
+
isStreaming: true,
|
|
56
|
+
toolCalls: [],
|
|
57
|
+
response: ''
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
for (let iteration = 0; iteration < MAX_ITERATIONS; iteration++) {
|
|
61
|
+
if (abortRef && abortRef.current) {
|
|
62
|
+
updateChatEntry(chatEntry, {
|
|
63
|
+
isStreaming: false,
|
|
64
|
+
response: accumulatedContent + '\n\n*(Agent stopped by user)*'
|
|
65
|
+
})
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const result = await callBackendAIchatWithTools(messages, config)
|
|
70
|
+
|
|
71
|
+
if (result.error) {
|
|
72
|
+
updateChatEntry(chatEntry, {
|
|
73
|
+
isStreaming: false,
|
|
74
|
+
response: accumulatedContent + `\n\n**Error:** ${result.error}`
|
|
75
|
+
})
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const assistantMessage = result.message
|
|
80
|
+
if (!assistantMessage) {
|
|
81
|
+
updateChatEntry(chatEntry, {
|
|
82
|
+
isStreaming: false,
|
|
83
|
+
response: accumulatedContent || 'No response from AI.'
|
|
84
|
+
})
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
messages.push(assistantMessage)
|
|
89
|
+
|
|
90
|
+
if (assistantMessage.content) {
|
|
91
|
+
accumulatedContent += (accumulatedContent ? '\n\n' : '') + assistantMessage.content
|
|
92
|
+
updateChatEntry(chatEntry, {
|
|
93
|
+
response: accumulatedContent
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!assistantMessage.tool_calls || assistantMessage.tool_calls.length === 0) {
|
|
98
|
+
updateChatEntry(chatEntry, {
|
|
99
|
+
isStreaming: false,
|
|
100
|
+
response: accumulatedContent
|
|
101
|
+
})
|
|
102
|
+
return
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const toolCall of assistantMessage.tool_calls) {
|
|
106
|
+
if (abortRef && abortRef.current) {
|
|
107
|
+
updateChatEntry(chatEntry, {
|
|
108
|
+
isStreaming: false,
|
|
109
|
+
response: accumulatedContent + '\n\n*(Agent stopped by user)*'
|
|
110
|
+
})
|
|
111
|
+
return
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let args
|
|
115
|
+
try {
|
|
116
|
+
args = JSON.parse(toolCall.function.arguments)
|
|
117
|
+
} catch {
|
|
118
|
+
args = {}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const toolEntry = {
|
|
122
|
+
id: toolCall.id,
|
|
123
|
+
name: toolCall.function.name,
|
|
124
|
+
args,
|
|
125
|
+
status: 'running',
|
|
126
|
+
result: null
|
|
127
|
+
}
|
|
128
|
+
toolCallsLog.push(toolEntry)
|
|
129
|
+
updateChatEntry(chatEntry, {
|
|
130
|
+
toolCalls: [...toolCallsLog]
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
let toolResult
|
|
134
|
+
try {
|
|
135
|
+
toolResult = await executeToolCall(toolCall.function.name, args)
|
|
136
|
+
toolEntry.status = 'completed'
|
|
137
|
+
toolEntry.result = toolResult
|
|
138
|
+
} catch (err) {
|
|
139
|
+
toolEntry.status = 'error'
|
|
140
|
+
toolEntry.result = err.message
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
updateChatEntry(chatEntry, {
|
|
144
|
+
toolCalls: [...toolCallsLog]
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
messages.push({
|
|
148
|
+
role: 'tool',
|
|
149
|
+
tool_call_id: toolCall.id,
|
|
150
|
+
content: toolEntry.result
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
updateChatEntry(chatEntry, {
|
|
156
|
+
isStreaming: false,
|
|
157
|
+
response: accumulatedContent + '\n\n*(Agent reached maximum iterations)*'
|
|
158
|
+
})
|
|
159
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useState, useEffect, useRef, useCallback } from 'react'
|
|
2
2
|
import AIOutput from './ai-output'
|
|
3
3
|
import AIStopIcon from './ai-stop-icon'
|
|
4
|
+
import AgentToolCallCard from './agent-tool-call-card'
|
|
5
|
+
import { runAgentLoop } from './agent'
|
|
4
6
|
import {
|
|
5
7
|
Alert,
|
|
6
8
|
Tooltip
|
|
@@ -17,6 +19,7 @@ import { copy } from '../../common/clipboard'
|
|
|
17
19
|
export default function AIChatHistoryItem ({ item }) {
|
|
18
20
|
const [showOutput, setShowOutput] = useState(true)
|
|
19
21
|
const startedRef = useRef(false)
|
|
22
|
+
const abortRef = useRef(false)
|
|
20
23
|
const {
|
|
21
24
|
prompt,
|
|
22
25
|
isStreaming,
|
|
@@ -28,7 +31,9 @@ export default function AIChatHistoryItem ({ item }) {
|
|
|
28
31
|
apiPathAI,
|
|
29
32
|
apiKeyAI,
|
|
30
33
|
proxyAI,
|
|
31
|
-
languageAI
|
|
34
|
+
languageAI,
|
|
35
|
+
mode,
|
|
36
|
+
toolCalls
|
|
32
37
|
} = item
|
|
33
38
|
|
|
34
39
|
function toggleOutput () {
|
|
@@ -108,15 +113,42 @@ export default function AIChatHistoryItem ({ item }) {
|
|
|
108
113
|
}
|
|
109
114
|
}, [prompt, modelAI, baseURLAI, apiPathAI, apiKeyAI, proxyAI, item.id, pollStreamContent])
|
|
110
115
|
|
|
116
|
+
const startAgentRequest = useCallback(async () => {
|
|
117
|
+
abortRef.current = false
|
|
118
|
+
const config = {
|
|
119
|
+
modelAI,
|
|
120
|
+
roleAI,
|
|
121
|
+
baseURLAI,
|
|
122
|
+
apiPathAI,
|
|
123
|
+
apiKeyAI,
|
|
124
|
+
proxyAI,
|
|
125
|
+
languageAI
|
|
126
|
+
}
|
|
127
|
+
await runAgentLoop(item, config, abortRef)
|
|
128
|
+
}, [modelAI, roleAI, baseURLAI, apiPathAI, apiKeyAI, proxyAI, languageAI, item.id])
|
|
129
|
+
|
|
111
130
|
useEffect(() => {
|
|
112
131
|
if (!response && !startedRef.current) {
|
|
113
132
|
startedRef.current = true
|
|
114
|
-
|
|
133
|
+
if (mode === 'agent') {
|
|
134
|
+
startAgentRequest()
|
|
135
|
+
} else {
|
|
136
|
+
startRequest()
|
|
137
|
+
}
|
|
115
138
|
}
|
|
116
139
|
}, [])
|
|
117
140
|
|
|
118
141
|
async function handleStop (e) {
|
|
119
142
|
e.stopPropagation()
|
|
143
|
+
if (mode === 'agent') {
|
|
144
|
+
abortRef.current = true
|
|
145
|
+
const index = window.store.aiChatHistory.findIndex(i => i.id === item.id)
|
|
146
|
+
if (index !== -1) {
|
|
147
|
+
window.store.aiChatHistory[index].isStreaming = false
|
|
148
|
+
window.store.aiChatHistory = [...window.store.aiChatHistory]
|
|
149
|
+
}
|
|
150
|
+
return
|
|
151
|
+
}
|
|
120
152
|
if (!sessionId) return
|
|
121
153
|
|
|
122
154
|
try {
|
|
@@ -194,6 +226,19 @@ export default function AIChatHistoryItem ({ item }) {
|
|
|
194
226
|
)
|
|
195
227
|
}
|
|
196
228
|
|
|
229
|
+
function renderToolCalls () {
|
|
230
|
+
if (mode !== 'agent' || !toolCalls || !toolCalls.length) {
|
|
231
|
+
return null
|
|
232
|
+
}
|
|
233
|
+
return (
|
|
234
|
+
<div className='agent-tool-calls'>
|
|
235
|
+
{toolCalls.map((tc) => (
|
|
236
|
+
<AgentToolCallCard key={tc.id} toolCall={tc} />
|
|
237
|
+
))}
|
|
238
|
+
</div>
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
|
|
197
242
|
return (
|
|
198
243
|
<div className='chat-history-item'>
|
|
199
244
|
<div className='mg1y'>
|
|
@@ -201,6 +246,7 @@ export default function AIChatHistoryItem ({ item }) {
|
|
|
201
246
|
<Alert {...alertProps} />
|
|
202
247
|
</Tooltip>
|
|
203
248
|
</div>
|
|
249
|
+
{renderToolCalls()}
|
|
204
250
|
{showOutput && <AIOutput item={item} />}
|
|
205
251
|
</div>
|
|
206
252
|
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useState, useCallback, useEffect } from 'react'
|
|
2
|
-
import { Flex, Input } from 'antd'
|
|
2
|
+
import { Flex, Input, Segmented } from 'antd'
|
|
3
3
|
import TabSelect from '../footer/tab-select'
|
|
4
4
|
import AiChatHistory from './ai-chat-history'
|
|
5
5
|
import uid from '../../common/uid'
|
|
@@ -21,6 +21,8 @@ const MAX_HISTORY = 100
|
|
|
21
21
|
|
|
22
22
|
export default function AIChat (props) {
|
|
23
23
|
const [prompt, setPrompt] = useState('')
|
|
24
|
+
const [mode, setMode] = useState('ask')
|
|
25
|
+
const isAgent = mode === 'agent'
|
|
24
26
|
|
|
25
27
|
function handlePromptChange (e) {
|
|
26
28
|
setPrompt(e.target.value)
|
|
@@ -38,6 +40,8 @@ export default function AIChat (props) {
|
|
|
38
40
|
response: '',
|
|
39
41
|
isStreaming: false,
|
|
40
42
|
sessionId: null,
|
|
43
|
+
mode,
|
|
44
|
+
toolCalls: [],
|
|
41
45
|
...pick(props.config, [
|
|
42
46
|
'modelAI',
|
|
43
47
|
'roleAI',
|
|
@@ -57,7 +61,7 @@ export default function AIChat (props) {
|
|
|
57
61
|
if (window.store.aiChatHistory.length > MAX_HISTORY) {
|
|
58
62
|
window.store.aiChatHistory.splice(MAX_HISTORY)
|
|
59
63
|
}
|
|
60
|
-
}, [prompt])
|
|
64
|
+
}, [prompt, mode])
|
|
61
65
|
|
|
62
66
|
function renderHistory () {
|
|
63
67
|
return (
|
|
@@ -75,6 +79,19 @@ export default function AIChat (props) {
|
|
|
75
79
|
window.store.aiChatHistory = []
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
function renderTabSelect () {
|
|
83
|
+
if (isAgent) {
|
|
84
|
+
return null
|
|
85
|
+
}
|
|
86
|
+
return (
|
|
87
|
+
<TabSelect
|
|
88
|
+
selectedTabIds={props.selectedTabIds}
|
|
89
|
+
tabs={props.tabs}
|
|
90
|
+
activeTabId={props.activeTabId}
|
|
91
|
+
/>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
78
95
|
function renderSendIcon () {
|
|
79
96
|
return (
|
|
80
97
|
<SendOutlined
|
|
@@ -126,11 +143,13 @@ export default function AIChat (props) {
|
|
|
126
143
|
/>
|
|
127
144
|
<Flex className='ai-chat-terminals' justify='space-between' align='center'>
|
|
128
145
|
<Flex align='center'>
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
146
|
+
<Segmented
|
|
147
|
+
options={['Ask', 'Agent']}
|
|
148
|
+
value={mode === 'ask' ? 'Ask' : 'Agent'}
|
|
149
|
+
onChange={(val) => setMode(val === 'Ask' ? 'ask' : 'agent')}
|
|
150
|
+
size='small'
|
|
133
151
|
/>
|
|
152
|
+
{renderTabSelect()}
|
|
134
153
|
<SettingOutlined
|
|
135
154
|
onClick={toggleConfig}
|
|
136
155
|
className='mg1l pointer icon-hover toggle-ai-setting-icon'
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
Alert,
|
|
7
7
|
Space
|
|
8
8
|
} from 'antd'
|
|
9
|
-
import { useEffect } from 'react'
|
|
9
|
+
import { useEffect, useState } from 'react'
|
|
10
10
|
import Link from '../common/external-link'
|
|
11
11
|
import AiCache from './ai-cache'
|
|
12
12
|
import {
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
} from '../../common/constants'
|
|
15
15
|
import Password from '../common/password'
|
|
16
16
|
import AiHistory, { addHistoryItem } from './ai-history'
|
|
17
|
+
import message from '../common/message'
|
|
17
18
|
|
|
18
19
|
const STORAGE_KEY_CONFIG = 'ai_config_history'
|
|
19
20
|
const EVENT_NAME_CONFIG = 'ai-config-history-update'
|
|
@@ -36,6 +37,7 @@ const proxyOptions = [
|
|
|
36
37
|
|
|
37
38
|
export default function AIConfigForm ({ initialValues, onSubmit, showAIConfig }) {
|
|
38
39
|
const [form] = Form.useForm()
|
|
40
|
+
const [testing, setTesting] = useState(false)
|
|
39
41
|
const baseURLAI = Form.useWatch('baseURLAI', form)
|
|
40
42
|
|
|
41
43
|
useEffect(() => {
|
|
@@ -53,6 +55,37 @@ export default function AIConfigForm ({ initialValues, onSubmit, showAIConfig })
|
|
|
53
55
|
addHistoryItem(STORAGE_KEY_CONFIG, values, EVENT_NAME_CONFIG)
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
const handleTest = async () => {
|
|
59
|
+
try {
|
|
60
|
+
const values = await form.validateFields()
|
|
61
|
+
setTesting(true)
|
|
62
|
+
const res = await window.pre.runGlobalAsync(
|
|
63
|
+
'AIchat',
|
|
64
|
+
'Hi',
|
|
65
|
+
values.modelAI,
|
|
66
|
+
values.roleAI,
|
|
67
|
+
values.baseURLAI,
|
|
68
|
+
values.apiPathAI,
|
|
69
|
+
values.apiKeyAI,
|
|
70
|
+
values.proxyAI,
|
|
71
|
+
false
|
|
72
|
+
)
|
|
73
|
+
if (res && res.error) {
|
|
74
|
+
message.error(res.error)
|
|
75
|
+
} else if (res && res.response) {
|
|
76
|
+
message.success('AI config works!')
|
|
77
|
+
} else {
|
|
78
|
+
message.error('Unexpected response from AI API')
|
|
79
|
+
}
|
|
80
|
+
} catch (e) {
|
|
81
|
+
if (e.message) {
|
|
82
|
+
message.error(e.message)
|
|
83
|
+
}
|
|
84
|
+
} finally {
|
|
85
|
+
setTesting(false)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
56
89
|
function handleSelectHistory (item) {
|
|
57
90
|
if (item && typeof item === 'object') {
|
|
58
91
|
form.setFieldsValue(item)
|
|
@@ -186,9 +219,17 @@ export default function AIConfigForm ({ initialValues, onSubmit, showAIConfig })
|
|
|
186
219
|
</Form.Item>
|
|
187
220
|
|
|
188
221
|
<Form.Item>
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
|
|
222
|
+
<Space>
|
|
223
|
+
<Button type='primary' htmlType='submit'>
|
|
224
|
+
{e('save')}
|
|
225
|
+
</Button>
|
|
226
|
+
<Button
|
|
227
|
+
loading={testing}
|
|
228
|
+
onClick={handleTest}
|
|
229
|
+
>
|
|
230
|
+
{e('testConnection')}
|
|
231
|
+
</Button>
|
|
232
|
+
</Space>
|
|
192
233
|
</Form.Item>
|
|
193
234
|
</Form>
|
|
194
235
|
<AiHistory
|
|
@@ -77,3 +77,76 @@
|
|
|
77
77
|
align-items center
|
|
78
78
|
.ai-stop-icon-square
|
|
79
79
|
margin-left auto
|
|
80
|
+
|
|
81
|
+
.agent-tool-calls
|
|
82
|
+
margin 8px 0
|
|
83
|
+
|
|
84
|
+
.agent-tool-call-card
|
|
85
|
+
border 1px solid var(--main-darker)
|
|
86
|
+
border-radius 4px
|
|
87
|
+
margin-bottom 6px
|
|
88
|
+
font-size 13px
|
|
89
|
+
|
|
90
|
+
.agent-tool-header
|
|
91
|
+
display flex
|
|
92
|
+
align-items center
|
|
93
|
+
padding 6px 8px
|
|
94
|
+
gap 6px
|
|
95
|
+
|
|
96
|
+
.agent-tool-name
|
|
97
|
+
font-weight 500
|
|
98
|
+
|
|
99
|
+
.agent-tool-tag
|
|
100
|
+
margin-left auto
|
|
101
|
+
|
|
102
|
+
.agent-tool-status-running
|
|
103
|
+
color #1890ff
|
|
104
|
+
animation spin 1s linear infinite
|
|
105
|
+
|
|
106
|
+
.agent-tool-status-completed
|
|
107
|
+
color #52c41a
|
|
108
|
+
|
|
109
|
+
.agent-tool-status-error
|
|
110
|
+
color #ff4d4f
|
|
111
|
+
|
|
112
|
+
.agent-tool-detail
|
|
113
|
+
padding 0 8px 8px
|
|
114
|
+
border-top 1px solid var(--main-darker)
|
|
115
|
+
|
|
116
|
+
.agent-tool-args
|
|
117
|
+
margin-top 6px
|
|
118
|
+
|
|
119
|
+
.agent-tool-result
|
|
120
|
+
margin-top 6px
|
|
121
|
+
|
|
122
|
+
.agent-tool-label
|
|
123
|
+
font-weight 500
|
|
124
|
+
margin-bottom 4px
|
|
125
|
+
font-size 12px
|
|
126
|
+
color var(--text-color-2)
|
|
127
|
+
|
|
128
|
+
.agent-tool-pre
|
|
129
|
+
background var(--main-dark)
|
|
130
|
+
padding 6px 8px
|
|
131
|
+
border-radius 3px
|
|
132
|
+
font-size 12px
|
|
133
|
+
max-height 200px
|
|
134
|
+
overflow-y auto
|
|
135
|
+
white-space pre-wrap
|
|
136
|
+
word-break break-all
|
|
137
|
+
margin-bottom 0
|
|
138
|
+
|
|
139
|
+
.agent-tool-running
|
|
140
|
+
border-color #1890ff
|
|
141
|
+
|
|
142
|
+
.agent-tool-completed
|
|
143
|
+
border-color #52c41a
|
|
144
|
+
|
|
145
|
+
.agent-tool-error
|
|
146
|
+
border-color #ff4d4f
|
|
147
|
+
|
|
148
|
+
@keyframes spin
|
|
149
|
+
from
|
|
150
|
+
transform rotate(0deg)
|
|
151
|
+
to
|
|
152
|
+
transform rotate(360deg)
|
|
@@ -4,7 +4,7 @@ import Layout from '../layout/layout'
|
|
|
4
4
|
import FileInfoModal from '../sftp/file-info-modal'
|
|
5
5
|
import UpdateCheck from './upgrade'
|
|
6
6
|
import SettingModal from '../setting-panel/setting-modal'
|
|
7
|
-
import TextEditor from '../text-editor/text-editor'
|
|
7
|
+
import TextEditor from '../text-editor/text-editor-entry'
|
|
8
8
|
import Sidebar from '../sidebar'
|
|
9
9
|
import CssOverwrite from '../bg/css-overwrite'
|
|
10
10
|
import UiTheme from './ui-theme'
|
|
@@ -19,7 +19,6 @@ import TransportsActionStore from '../file-transfer/transports-action-store.jsx'
|
|
|
19
19
|
import classnames from 'classnames'
|
|
20
20
|
import ShortcutControl from '../shortcuts/shortcut-control.jsx'
|
|
21
21
|
import { isMac, isWin, textTerminalBgValue } from '../../common/constants'
|
|
22
|
-
import TerminalInfo from '../terminal-info/terminal-info'
|
|
23
22
|
import { ConfigProvider } from 'antd'
|
|
24
23
|
import { NotificationContainer } from '../common/notification'
|
|
25
24
|
import InfoModal from '../sidebar/info-modal.jsx'
|
|
@@ -27,7 +26,7 @@ import RightSidePanel from '../side-panel-r/side-panel-r'
|
|
|
27
26
|
import ConnectionHoppingWarning from './connection-hopping-warnning'
|
|
28
27
|
import SshConfigLoadNotify from '../ssh-config/ssh-config-load-notify'
|
|
29
28
|
import LoadSshConfigs from '../ssh-config/load-ssh-configs'
|
|
30
|
-
import AIChat from '../ai/ai-chat'
|
|
29
|
+
import AIChat from '../ai/ai-chat-entry'
|
|
31
30
|
import AIConfigModal from '../ai/ai-config-modal'
|
|
32
31
|
import Opacity from '../common/opacity'
|
|
33
32
|
import MoveItemModal from '../tree-list/move-item-modal'
|
|
@@ -40,6 +39,7 @@ import UnixTimestampTooltip from '../terminal/unix-timestamp-tooltip'
|
|
|
40
39
|
import { pick } from 'lodash-es'
|
|
41
40
|
import deepCopy from 'json-deep-copy'
|
|
42
41
|
import './wrapper.styl'
|
|
42
|
+
import TerminalInfo from '../terminal-info/terminal-info-entry'
|
|
43
43
|
import './term-fullscreen.styl'
|
|
44
44
|
|
|
45
45
|
export default auto(function Index (props) {
|
|
@@ -273,6 +273,9 @@ export class FileTransferManager {
|
|
|
273
273
|
})
|
|
274
274
|
|
|
275
275
|
this.log(`Downloaded ${fileInfo.name} (${filesize(fileInfo._totalSize)}) to ${fullPath}`, 'success')
|
|
276
|
+
this.hasRemoteFiles = false
|
|
277
|
+
this.pendingDownloads.clear()
|
|
278
|
+
this.notifyStateChange()
|
|
276
279
|
if (this.onDownloadComplete) {
|
|
277
280
|
this.onDownloadComplete(fullPath, fileInfo.name, fileInfo._totalSize)
|
|
278
281
|
}
|