@electerm/electerm-react 2.3.151 → 2.3.176
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/download.jsx +1 -1
- package/client/common/error-handler.jsx +1 -1
- package/client/common/fetch.jsx +1 -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/connection-hopping-warnning.jsx +1 -1
- package/client/components/main/main.jsx +2 -0
- package/client/components/quick-commands/qm.styl +0 -10
- package/client/components/quick-commands/quick-command-item.jsx +2 -5
- package/client/components/quick-commands/quick-commands-box.jsx +12 -23
- package/client/components/setting-panel/setting-common.jsx +4 -3
- package/client/components/setting-panel/setting-modal.jsx +2 -1
- 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/setting-sync/setting-sync-form.jsx +1 -1
- package/client/components/sftp/file-info-modal.jsx +2 -1
- package/client/components/sftp/file-item.jsx +2 -0
- package/client/components/sftp/sftp-entry.jsx +1 -1
- package/client/components/sftp/sftp.styl +1 -1
- package/client/components/sidebar/info-modal.jsx +53 -34
- package/client/components/sidebar/info.styl +0 -7
- package/client/components/ssh-config/ssh-config-load-notify.jsx +1 -1
- 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 +1 -2
- 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 +12 -6
- package/client/components/widgets/widget-form.jsx +16 -18
- 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/css/includes/box.styl +2 -2
- package/client/store/common.js +9 -5
- package/client/store/init-state.js +4 -0
- package/client/store/load-data.js +15 -6
- package/client/store/mcp-handler.js +640 -0
- package/client/store/store.js +4 -0
- package/client/store/widgets.js +4 -0
- package/client/store/workspace.js +108 -0
- package/package.json +1 -1
|
@@ -115,13 +115,22 @@ export async function addTabFromCommandLine (store, opts) {
|
|
|
115
115
|
export default (Store) => {
|
|
116
116
|
Store.prototype.openInitSessions = function () {
|
|
117
117
|
const { store } = window
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
118
|
+
const onStartSessions = store.config.onStartSessions
|
|
119
|
+
|
|
120
|
+
// If onStartSessions is a string, it's a workspace ID
|
|
121
|
+
if (typeof onStartSessions === 'string' && onStartSessions) {
|
|
122
|
+
store.loadWorkspace(onStartSessions)
|
|
123
|
+
} else {
|
|
124
|
+
// Otherwise, it's an array of bookmark IDs
|
|
125
|
+
const arr = Array.isArray(onStartSessions) ? onStartSessions : []
|
|
126
|
+
for (const s of arr) {
|
|
127
|
+
store.onSelectBookmark(s)
|
|
128
|
+
}
|
|
129
|
+
if (!arr.length && store.config.initDefaultTabOnStart) {
|
|
130
|
+
store.initFirstTab()
|
|
131
|
+
}
|
|
124
132
|
}
|
|
133
|
+
|
|
125
134
|
store.confirmLoad()
|
|
126
135
|
const { initTime, loadTime } = window.pre.runSync('getLoadTime')
|
|
127
136
|
if (loadTime) {
|
|
@@ -0,0 +1,640 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP (Model Context Protocol) handler for store
|
|
3
|
+
* Handles IPC requests from the MCP server widget
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import uid from '../common/uid'
|
|
7
|
+
import { settingMap } from '../common/constants'
|
|
8
|
+
|
|
9
|
+
export default Store => {
|
|
10
|
+
// Initialize MCP handler - called when MCP widget is started
|
|
11
|
+
Store.prototype.initMcpHandler = function () {
|
|
12
|
+
const { ipcOnEvent } = window.pre
|
|
13
|
+
// Listen for MCP requests from main process
|
|
14
|
+
ipcOnEvent('mcp-request', (event, request) => {
|
|
15
|
+
const { requestId, action, data } = request
|
|
16
|
+
if (action === 'tool-call') {
|
|
17
|
+
window.store.handleMcpToolCall(requestId, data.toolName, data.args)
|
|
18
|
+
}
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Handle individual tool calls
|
|
23
|
+
Store.prototype.handleMcpToolCall = async function (requestId, toolName, args) {
|
|
24
|
+
const { store } = window
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
let result
|
|
28
|
+
|
|
29
|
+
switch (toolName) {
|
|
30
|
+
// Bookmark operations
|
|
31
|
+
case 'list_bookmarks':
|
|
32
|
+
result = store.mcpListBookmarks(args)
|
|
33
|
+
break
|
|
34
|
+
case 'get_bookmark':
|
|
35
|
+
result = store.mcpGetBookmark(args)
|
|
36
|
+
break
|
|
37
|
+
case 'add_bookmark':
|
|
38
|
+
result = await store.mcpAddBookmark(args)
|
|
39
|
+
break
|
|
40
|
+
case 'edit_bookmark':
|
|
41
|
+
result = store.mcpEditBookmark(args)
|
|
42
|
+
break
|
|
43
|
+
case 'delete_bookmark':
|
|
44
|
+
result = store.mcpDeleteBookmark(args)
|
|
45
|
+
break
|
|
46
|
+
case 'open_bookmark':
|
|
47
|
+
result = store.mcpOpenBookmark(args)
|
|
48
|
+
break
|
|
49
|
+
|
|
50
|
+
// Bookmark group operations
|
|
51
|
+
case 'list_bookmark_groups':
|
|
52
|
+
result = store.mcpListBookmarkGroups()
|
|
53
|
+
break
|
|
54
|
+
case 'add_bookmark_group':
|
|
55
|
+
result = await store.mcpAddBookmarkGroup(args)
|
|
56
|
+
break
|
|
57
|
+
|
|
58
|
+
// Quick command operations
|
|
59
|
+
case 'list_quick_commands':
|
|
60
|
+
result = store.mcpListQuickCommands()
|
|
61
|
+
break
|
|
62
|
+
case 'add_quick_command':
|
|
63
|
+
result = store.mcpAddQuickCommand(args)
|
|
64
|
+
break
|
|
65
|
+
case 'run_quick_command':
|
|
66
|
+
result = store.mcpRunQuickCommand(args)
|
|
67
|
+
break
|
|
68
|
+
case 'delete_quick_command':
|
|
69
|
+
result = store.mcpDeleteQuickCommand(args)
|
|
70
|
+
break
|
|
71
|
+
|
|
72
|
+
// Tab operations
|
|
73
|
+
case 'list_tabs':
|
|
74
|
+
result = store.mcpListTabs()
|
|
75
|
+
break
|
|
76
|
+
case 'get_active_tab':
|
|
77
|
+
result = store.mcpGetActiveTab()
|
|
78
|
+
break
|
|
79
|
+
case 'switch_tab':
|
|
80
|
+
result = store.mcpSwitchTab(args)
|
|
81
|
+
break
|
|
82
|
+
case 'close_tab':
|
|
83
|
+
result = store.mcpCloseTab(args)
|
|
84
|
+
break
|
|
85
|
+
case 'reload_tab':
|
|
86
|
+
result = store.mcpReloadTab(args)
|
|
87
|
+
break
|
|
88
|
+
case 'duplicate_tab':
|
|
89
|
+
result = store.mcpDuplicateTab(args)
|
|
90
|
+
break
|
|
91
|
+
case 'open_local_terminal':
|
|
92
|
+
result = store.mcpOpenLocalTerminal()
|
|
93
|
+
break
|
|
94
|
+
|
|
95
|
+
// Terminal operations
|
|
96
|
+
case 'send_terminal_command':
|
|
97
|
+
result = store.mcpSendTerminalCommand(args)
|
|
98
|
+
break
|
|
99
|
+
case 'get_terminal_selection':
|
|
100
|
+
result = store.mcpGetTerminalSelection(args)
|
|
101
|
+
break
|
|
102
|
+
case 'get_terminal_output':
|
|
103
|
+
result = store.mcpGetTerminalOutput(args)
|
|
104
|
+
break
|
|
105
|
+
|
|
106
|
+
// History operations
|
|
107
|
+
case 'list_history':
|
|
108
|
+
result = store.mcpListHistory(args)
|
|
109
|
+
break
|
|
110
|
+
case 'clear_history':
|
|
111
|
+
result = store.mcpClearHistory()
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
// Transfer operations
|
|
115
|
+
case 'list_transfers':
|
|
116
|
+
result = store.mcpListTransfers()
|
|
117
|
+
break
|
|
118
|
+
case 'list_transfer_history':
|
|
119
|
+
result = store.mcpListTransferHistory(args)
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
// Settings operations
|
|
123
|
+
case 'get_settings':
|
|
124
|
+
result = store.mcpGetSettings()
|
|
125
|
+
break
|
|
126
|
+
case 'list_terminal_themes':
|
|
127
|
+
result = store.mcpListTerminalThemes()
|
|
128
|
+
break
|
|
129
|
+
case 'list_ui_themes':
|
|
130
|
+
result = store.mcpListUiThemes()
|
|
131
|
+
break
|
|
132
|
+
|
|
133
|
+
default:
|
|
134
|
+
throw new Error(`Unknown tool: ${toolName}`)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
window.api.sendMcpResponse({
|
|
138
|
+
requestId,
|
|
139
|
+
result
|
|
140
|
+
})
|
|
141
|
+
} catch (error) {
|
|
142
|
+
window.api.sendMcpResponse({
|
|
143
|
+
requestId,
|
|
144
|
+
error: error.message
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ==================== Bookmark APIs ====================
|
|
150
|
+
|
|
151
|
+
Store.prototype.mcpListBookmarks = function (args = {}) {
|
|
152
|
+
const { store } = window
|
|
153
|
+
let bookmarks = store.bookmarks
|
|
154
|
+
|
|
155
|
+
if (args.groupId) {
|
|
156
|
+
const group = store.bookmarkGroups.find(g => g.id === args.groupId)
|
|
157
|
+
if (group && group.bookmarkIds) {
|
|
158
|
+
const idSet = new Set(group.bookmarkIds)
|
|
159
|
+
bookmarks = bookmarks.filter(b => idSet.has(b.id))
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return bookmarks.map(b => ({
|
|
164
|
+
id: b.id,
|
|
165
|
+
title: b.title,
|
|
166
|
+
host: b.host,
|
|
167
|
+
port: b.port,
|
|
168
|
+
username: b.username,
|
|
169
|
+
type: b.type || 'ssh',
|
|
170
|
+
color: b.color
|
|
171
|
+
}))
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
Store.prototype.mcpGetBookmark = function (args) {
|
|
175
|
+
const { store } = window
|
|
176
|
+
const bookmark = store.bookmarks.find(b => b.id === args.id)
|
|
177
|
+
if (!bookmark) {
|
|
178
|
+
throw new Error(`Bookmark not found: ${args.id}`)
|
|
179
|
+
}
|
|
180
|
+
// Return bookmark without sensitive data
|
|
181
|
+
const { password, passphrase, privateKey, ...safeBookmark } = bookmark
|
|
182
|
+
return safeBookmark
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
Store.prototype.mcpAddBookmark = async function (args) {
|
|
186
|
+
const { store } = window
|
|
187
|
+
const bookmark = {
|
|
188
|
+
id: uid(),
|
|
189
|
+
title: args.title,
|
|
190
|
+
host: args.host || '',
|
|
191
|
+
port: args.port || 22,
|
|
192
|
+
username: args.username || '',
|
|
193
|
+
password: args.password || '',
|
|
194
|
+
type: args.type || 'local',
|
|
195
|
+
term: 'xterm-256color',
|
|
196
|
+
...args
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
store.addItem(bookmark, settingMap.bookmarks)
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
success: true,
|
|
203
|
+
id: bookmark.id,
|
|
204
|
+
message: `Bookmark "${bookmark.title}" created`
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
Store.prototype.mcpEditBookmark = function (args) {
|
|
209
|
+
const { store } = window
|
|
210
|
+
const { id, updates } = args
|
|
211
|
+
|
|
212
|
+
const bookmark = store.bookmarks.find(b => b.id === id)
|
|
213
|
+
if (!bookmark) {
|
|
214
|
+
throw new Error(`Bookmark not found: ${id}`)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
store.editItem(id, updates, settingMap.bookmarks)
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
success: true,
|
|
221
|
+
message: `Bookmark "${bookmark.title}" updated`
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
Store.prototype.mcpDeleteBookmark = function (args) {
|
|
226
|
+
const { store } = window
|
|
227
|
+
const bookmark = store.bookmarks.find(b => b.id === args.id)
|
|
228
|
+
if (!bookmark) {
|
|
229
|
+
throw new Error(`Bookmark not found: ${args.id}`)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
store.delItem({ id: args.id }, settingMap.bookmarks)
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
success: true,
|
|
236
|
+
message: `Bookmark "${bookmark.title}" deleted`
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
Store.prototype.mcpOpenBookmark = function (args) {
|
|
241
|
+
const { store } = window
|
|
242
|
+
const bookmark = store.bookmarks.find(b => b.id === args.id)
|
|
243
|
+
if (!bookmark) {
|
|
244
|
+
throw new Error(`Bookmark not found: ${args.id}`)
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
store.onSelectBookmark(args.id)
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
success: true,
|
|
251
|
+
message: `Opened bookmark "${bookmark.title}"`
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ==================== Bookmark Group APIs ====================
|
|
256
|
+
|
|
257
|
+
Store.prototype.mcpListBookmarkGroups = function () {
|
|
258
|
+
const { store } = window
|
|
259
|
+
return store.bookmarkGroups.map(g => ({
|
|
260
|
+
id: g.id,
|
|
261
|
+
title: g.title,
|
|
262
|
+
level: g.level,
|
|
263
|
+
bookmarkCount: (g.bookmarkIds || []).length,
|
|
264
|
+
subgroupCount: (g.bookmarkGroupIds || []).length
|
|
265
|
+
}))
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
Store.prototype.mcpAddBookmarkGroup = async function (args) {
|
|
269
|
+
const { store } = window
|
|
270
|
+
const group = {
|
|
271
|
+
id: uid(),
|
|
272
|
+
title: args.title,
|
|
273
|
+
bookmarkIds: [],
|
|
274
|
+
bookmarkGroupIds: [],
|
|
275
|
+
level: args.parentId ? 2 : 1
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
await store.addBookmarkGroup(group)
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
success: true,
|
|
282
|
+
id: group.id,
|
|
283
|
+
message: `Bookmark group "${group.title}" created`
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// ==================== Quick Command APIs ====================
|
|
288
|
+
|
|
289
|
+
Store.prototype.mcpListQuickCommands = function () {
|
|
290
|
+
const { store } = window
|
|
291
|
+
return store.quickCommands.map(q => ({
|
|
292
|
+
id: q.id,
|
|
293
|
+
name: q.name,
|
|
294
|
+
command: q.command,
|
|
295
|
+
commands: q.commands,
|
|
296
|
+
inputOnly: q.inputOnly,
|
|
297
|
+
labels: q.labels
|
|
298
|
+
}))
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
Store.prototype.mcpAddQuickCommand = function (args) {
|
|
302
|
+
const { store } = window
|
|
303
|
+
const qm = {
|
|
304
|
+
id: uid(),
|
|
305
|
+
name: args.name,
|
|
306
|
+
command: args.command,
|
|
307
|
+
inputOnly: args.inputOnly || false,
|
|
308
|
+
labels: args.labels || []
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
store.addQuickCommand(qm)
|
|
312
|
+
|
|
313
|
+
return {
|
|
314
|
+
success: true,
|
|
315
|
+
id: qm.id,
|
|
316
|
+
message: `Quick command "${qm.name}" created`
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
Store.prototype.mcpRunQuickCommand = function (args) {
|
|
321
|
+
const { store } = window
|
|
322
|
+
const qm = store.quickCommands.find(q => q.id === args.id)
|
|
323
|
+
if (!qm) {
|
|
324
|
+
throw new Error(`Quick command not found: ${args.id}`)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
store.runQuickCommandItem(args.id)
|
|
328
|
+
|
|
329
|
+
return {
|
|
330
|
+
success: true,
|
|
331
|
+
message: `Executed quick command "${qm.name}"`
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
Store.prototype.mcpDeleteQuickCommand = function (args) {
|
|
336
|
+
const { store } = window
|
|
337
|
+
const qm = store.quickCommands.find(q => q.id === args.id)
|
|
338
|
+
if (!qm) {
|
|
339
|
+
throw new Error(`Quick command not found: ${args.id}`)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
store.delQuickCommand({ id: args.id })
|
|
343
|
+
|
|
344
|
+
return {
|
|
345
|
+
success: true,
|
|
346
|
+
message: `Deleted quick command "${qm.name}"`
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ==================== Tab APIs ====================
|
|
351
|
+
|
|
352
|
+
Store.prototype.mcpListTabs = function () {
|
|
353
|
+
const { store } = window
|
|
354
|
+
return store.tabs.map(t => ({
|
|
355
|
+
id: t.id,
|
|
356
|
+
title: t.title,
|
|
357
|
+
host: t.host,
|
|
358
|
+
type: t.type || 'local',
|
|
359
|
+
status: t.status,
|
|
360
|
+
isTransporting: t.isTransporting,
|
|
361
|
+
batch: t.batch
|
|
362
|
+
}))
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
Store.prototype.mcpGetActiveTab = function () {
|
|
366
|
+
const { store } = window
|
|
367
|
+
const tab = store.currentTab
|
|
368
|
+
if (!tab) {
|
|
369
|
+
return { activeTabId: null, tab: null }
|
|
370
|
+
}
|
|
371
|
+
return {
|
|
372
|
+
activeTabId: store.activeTabId,
|
|
373
|
+
tab: {
|
|
374
|
+
id: tab.id,
|
|
375
|
+
title: tab.title,
|
|
376
|
+
host: tab.host,
|
|
377
|
+
type: tab.type || 'local',
|
|
378
|
+
status: tab.status
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
Store.prototype.mcpSwitchTab = function (args) {
|
|
384
|
+
const { store } = window
|
|
385
|
+
const tab = store.tabs.find(t => t.id === args.tabId)
|
|
386
|
+
if (!tab) {
|
|
387
|
+
throw new Error(`Tab not found: ${args.tabId}`)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
store.activeTabId = args.tabId
|
|
391
|
+
if (tab.batch !== undefined) {
|
|
392
|
+
store[`activeTabId${tab.batch}`] = args.tabId
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
success: true,
|
|
397
|
+
message: `Switched to tab "${tab.title}"`
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
Store.prototype.mcpCloseTab = function (args) {
|
|
402
|
+
const { store } = window
|
|
403
|
+
const tab = store.tabs.find(t => t.id === args.tabId)
|
|
404
|
+
if (!tab) {
|
|
405
|
+
throw new Error(`Tab not found: ${args.tabId}`)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
store.delTab(args.tabId)
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
success: true,
|
|
412
|
+
message: `Closed tab "${tab.title}"`
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
Store.prototype.mcpReloadTab = function (args) {
|
|
417
|
+
const { store } = window
|
|
418
|
+
const tabId = args.tabId || store.activeTabId
|
|
419
|
+
const tab = store.tabs.find(t => t.id === tabId)
|
|
420
|
+
if (!tab) {
|
|
421
|
+
throw new Error(`Tab not found: ${tabId}`)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
store.reloadTab(tabId)
|
|
425
|
+
|
|
426
|
+
return {
|
|
427
|
+
success: true,
|
|
428
|
+
message: `Reloaded tab "${tab.title}"`
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
Store.prototype.mcpDuplicateTab = function (args) {
|
|
433
|
+
const { store } = window
|
|
434
|
+
const tab = store.tabs.find(t => t.id === args.tabId)
|
|
435
|
+
if (!tab) {
|
|
436
|
+
throw new Error(`Tab not found: ${args.tabId}`)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
store.duplicateTab(args.tabId)
|
|
440
|
+
|
|
441
|
+
return {
|
|
442
|
+
success: true,
|
|
443
|
+
message: `Duplicated tab "${tab.title}"`
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
Store.prototype.mcpOpenLocalTerminal = function () {
|
|
448
|
+
const { store } = window
|
|
449
|
+
store.addTab()
|
|
450
|
+
const newTabId = store.activeTabId
|
|
451
|
+
|
|
452
|
+
return {
|
|
453
|
+
success: true,
|
|
454
|
+
tabId: newTabId,
|
|
455
|
+
message: 'Opened new local terminal'
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// ==================== Terminal APIs ====================
|
|
460
|
+
|
|
461
|
+
Store.prototype.mcpSendTerminalCommand = function (args) {
|
|
462
|
+
const { store } = window
|
|
463
|
+
const tabId = args.tabId || store.activeTabId
|
|
464
|
+
const command = args.command
|
|
465
|
+
|
|
466
|
+
if (!tabId) {
|
|
467
|
+
throw new Error('No active terminal')
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
if (command === undefined || command === null) {
|
|
471
|
+
throw new Error('No command provided')
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
store.runQuickCommand(command, args.inputOnly || false)
|
|
475
|
+
|
|
476
|
+
return {
|
|
477
|
+
success: true,
|
|
478
|
+
message: 'Command sent to terminal'
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
Store.prototype.mcpGetTerminalSelection = function (args) {
|
|
483
|
+
const { store } = window
|
|
484
|
+
const { refs } = require('../components/common/ref')
|
|
485
|
+
const tabId = args.tabId || store.activeTabId
|
|
486
|
+
|
|
487
|
+
if (!tabId) {
|
|
488
|
+
throw new Error('No active terminal')
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const term = refs.get('term-' + tabId)
|
|
492
|
+
if (!term || !term.term) {
|
|
493
|
+
throw new Error('Terminal not found')
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const selection = term.term.getSelection()
|
|
497
|
+
|
|
498
|
+
return {
|
|
499
|
+
selection: selection || '',
|
|
500
|
+
tabId
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
Store.prototype.mcpGetTerminalOutput = function (args) {
|
|
505
|
+
const { store } = window
|
|
506
|
+
const { refs } = require('../components/common/ref')
|
|
507
|
+
const tabId = args.tabId || store.activeTabId
|
|
508
|
+
const lineCount = args.lines || 50
|
|
509
|
+
|
|
510
|
+
if (!tabId) {
|
|
511
|
+
throw new Error('No active terminal')
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const term = refs.get('term-' + tabId)
|
|
515
|
+
if (!term || !term.term) {
|
|
516
|
+
throw new Error('Terminal not found')
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const buffer = term.term.buffer.active
|
|
520
|
+
if (!buffer) {
|
|
521
|
+
throw new Error('Terminal buffer not available')
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
const cursorY = buffer.cursorY || 0
|
|
525
|
+
const baseY = buffer.baseY || 0
|
|
526
|
+
const totalLines = buffer.length || 0
|
|
527
|
+
|
|
528
|
+
// Calculate the actual content range
|
|
529
|
+
// baseY is the scroll offset, cursorY is cursor position in viewport
|
|
530
|
+
const actualContentEnd = baseY + cursorY + 1
|
|
531
|
+
const startLine = Math.max(0, actualContentEnd - lineCount)
|
|
532
|
+
const endLine = Math.min(totalLines, actualContentEnd)
|
|
533
|
+
const lines = []
|
|
534
|
+
|
|
535
|
+
for (let i = startLine; i < endLine; i++) {
|
|
536
|
+
const line = buffer.getLine(i)
|
|
537
|
+
if (line) {
|
|
538
|
+
const text = line.translateToString(true)
|
|
539
|
+
lines.push(text)
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
return {
|
|
544
|
+
output: lines.join('\n'),
|
|
545
|
+
lineCount: lines.length,
|
|
546
|
+
cursorY,
|
|
547
|
+
baseY,
|
|
548
|
+
tabId
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// ==================== History APIs ====================
|
|
553
|
+
|
|
554
|
+
Store.prototype.mcpListHistory = function (args = {}) {
|
|
555
|
+
const { store } = window
|
|
556
|
+
const limit = args.limit || 50
|
|
557
|
+
const history = store.history.slice(0, limit)
|
|
558
|
+
|
|
559
|
+
return history.map(h => ({
|
|
560
|
+
id: h.id,
|
|
561
|
+
title: h.title,
|
|
562
|
+
host: h.host,
|
|
563
|
+
type: h.type,
|
|
564
|
+
time: h.time
|
|
565
|
+
}))
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
Store.prototype.mcpClearHistory = function () {
|
|
569
|
+
const { store } = window
|
|
570
|
+
store.history = []
|
|
571
|
+
|
|
572
|
+
return {
|
|
573
|
+
success: true,
|
|
574
|
+
message: 'History cleared'
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// ==================== Transfer APIs ====================
|
|
579
|
+
|
|
580
|
+
Store.prototype.mcpListTransfers = function () {
|
|
581
|
+
const { store } = window
|
|
582
|
+
return store.fileTransfers.map(t => ({
|
|
583
|
+
id: t.id,
|
|
584
|
+
localPath: t.localPath,
|
|
585
|
+
remotePath: t.remotePath,
|
|
586
|
+
type: t.type,
|
|
587
|
+
percent: t.percent,
|
|
588
|
+
status: t.status
|
|
589
|
+
}))
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
Store.prototype.mcpListTransferHistory = function (args = {}) {
|
|
593
|
+
const { store } = window
|
|
594
|
+
const limit = args.limit || 50
|
|
595
|
+
return store.transferHistory.slice(0, limit).map(t => ({
|
|
596
|
+
id: t.id,
|
|
597
|
+
localPath: t.localPath,
|
|
598
|
+
remotePath: t.remotePath,
|
|
599
|
+
type: t.type,
|
|
600
|
+
status: t.status,
|
|
601
|
+
time: t.time
|
|
602
|
+
}))
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// ==================== Settings APIs ====================
|
|
606
|
+
|
|
607
|
+
Store.prototype.mcpGetSettings = function () {
|
|
608
|
+
const { store } = window
|
|
609
|
+
// Return safe settings (no sensitive data)
|
|
610
|
+
const config = store.config
|
|
611
|
+
const safeConfig = {
|
|
612
|
+
theme: config.theme,
|
|
613
|
+
language: config.language,
|
|
614
|
+
fontSize: config.fontSize,
|
|
615
|
+
fontFamily: config.fontFamily,
|
|
616
|
+
terminalType: config.terminalType,
|
|
617
|
+
cursorStyle: config.cursorStyle,
|
|
618
|
+
cursorBlink: config.cursorBlink,
|
|
619
|
+
scrollback: config.scrollback
|
|
620
|
+
}
|
|
621
|
+
return safeConfig
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
Store.prototype.mcpListTerminalThemes = function () {
|
|
625
|
+
const { store } = window
|
|
626
|
+
return store.terminalThemes.map(t => ({
|
|
627
|
+
id: t.id,
|
|
628
|
+
name: t.name,
|
|
629
|
+
themeLight: t.themeLight
|
|
630
|
+
}))
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
Store.prototype.mcpListUiThemes = function () {
|
|
634
|
+
const { store } = window
|
|
635
|
+
return (store.uiThemes || []).map(t => ({
|
|
636
|
+
id: t.id,
|
|
637
|
+
name: t.name
|
|
638
|
+
}))
|
|
639
|
+
}
|
|
640
|
+
}
|
package/client/store/store.js
CHANGED
|
@@ -25,6 +25,8 @@ import batchInputHistory from './batch-input-history'
|
|
|
25
25
|
import transferExtend from './transfer-list'
|
|
26
26
|
import addressBookmarkExtend from './address-bookmark'
|
|
27
27
|
import widgetsExtend from './widgets'
|
|
28
|
+
import mcpHandlerExtend from './mcp-handler'
|
|
29
|
+
import workspaceExtend from './workspace'
|
|
28
30
|
import isColorDark from '../common/is-color-dark'
|
|
29
31
|
import { getReverseColor } from '../common/reverse-color'
|
|
30
32
|
import { uniq } from 'lodash-es'
|
|
@@ -297,5 +299,7 @@ batchInputHistory(Store)
|
|
|
297
299
|
transferExtend(Store)
|
|
298
300
|
addressBookmarkExtend(Store)
|
|
299
301
|
widgetsExtend(Store)
|
|
302
|
+
mcpHandlerExtend(Store)
|
|
303
|
+
workspaceExtend(Store)
|
|
300
304
|
|
|
301
305
|
export const StateStore = Store
|
package/client/store/widgets.js
CHANGED
|
@@ -16,6 +16,10 @@ export default Store => {
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
Store.prototype.runWidget = async (widgetId, config) => {
|
|
19
|
+
// If this is MCP server widget, initialize MCP handler first
|
|
20
|
+
if (widgetId === 'mcp-server') {
|
|
21
|
+
window.store.initMcpHandler()
|
|
22
|
+
}
|
|
19
23
|
return window.pre.runGlobalAsync('runWidget', widgetId, config)
|
|
20
24
|
}
|
|
21
25
|
|