@jupyterlite/ai 0.8.0 → 0.9.0-a0
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/lib/agent.d.ts +233 -0
- package/lib/agent.js +604 -0
- package/lib/chat-model.d.ts +195 -0
- package/lib/chat-model.js +590 -0
- package/lib/completion/completion-provider.d.ts +83 -0
- package/lib/completion/completion-provider.js +209 -0
- package/lib/completion/index.d.ts +1 -0
- package/lib/completion/index.js +1 -0
- package/lib/components/clear-button.d.ts +18 -0
- package/lib/components/clear-button.js +31 -0
- package/lib/components/index.d.ts +3 -0
- package/lib/components/index.js +3 -0
- package/lib/components/model-select.d.ts +19 -0
- package/lib/components/model-select.js +154 -0
- package/lib/components/stop-button.d.ts +3 -3
- package/lib/components/stop-button.js +8 -9
- package/lib/components/token-usage-display.d.ts +45 -0
- package/lib/components/token-usage-display.js +74 -0
- package/lib/components/tool-select.d.ts +27 -0
- package/lib/components/tool-select.js +130 -0
- package/lib/icons.d.ts +3 -1
- package/lib/icons.js +10 -13
- package/lib/index.d.ts +4 -5
- package/lib/index.js +322 -167
- package/lib/mcp/browser.d.ts +68 -0
- package/lib/mcp/browser.js +132 -0
- package/lib/models/settings-model.d.ts +69 -0
- package/lib/models/settings-model.js +295 -0
- package/lib/providers/built-in-providers.d.ts +9 -0
- package/lib/providers/built-in-providers.js +192 -0
- package/lib/providers/models.d.ts +37 -0
- package/lib/providers/models.js +28 -0
- package/lib/providers/provider-registry.d.ts +94 -0
- package/lib/providers/provider-registry.js +155 -0
- package/lib/tokens.d.ts +157 -86
- package/lib/tokens.js +16 -12
- package/lib/tools/commands.d.ts +11 -0
- package/lib/tools/commands.js +126 -0
- package/lib/tools/file.d.ts +27 -0
- package/lib/tools/file.js +262 -0
- package/lib/tools/notebook.d.ts +40 -0
- package/lib/tools/notebook.js +762 -0
- package/lib/tools/tool-registry.d.ts +35 -0
- package/lib/tools/tool-registry.js +55 -0
- package/lib/widgets/ai-settings.d.ts +39 -0
- package/lib/widgets/ai-settings.js +506 -0
- package/lib/widgets/chat-wrapper.d.ts +144 -0
- package/lib/widgets/chat-wrapper.js +390 -0
- package/lib/widgets/provider-config-dialog.d.ts +13 -0
- package/lib/widgets/provider-config-dialog.js +104 -0
- package/package.json +150 -41
- package/schema/settings-model.json +153 -0
- package/src/agent.ts +800 -0
- package/src/chat-model.ts +770 -0
- package/src/completion/completion-provider.ts +308 -0
- package/src/completion/index.ts +1 -0
- package/src/components/clear-button.tsx +56 -0
- package/src/components/index.ts +3 -0
- package/src/components/model-select.tsx +245 -0
- package/src/components/stop-button.tsx +11 -11
- package/src/components/token-usage-display.tsx +130 -0
- package/src/components/tool-select.tsx +218 -0
- package/src/icons.ts +12 -14
- package/src/index.ts +468 -238
- package/src/mcp/browser.ts +213 -0
- package/src/models/settings-model.ts +409 -0
- package/src/providers/built-in-providers.ts +216 -0
- package/src/providers/models.ts +79 -0
- package/src/providers/provider-registry.ts +189 -0
- package/src/tokens.ts +203 -90
- package/src/tools/commands.ts +151 -0
- package/src/tools/file.ts +307 -0
- package/src/tools/notebook.ts +964 -0
- package/src/tools/tool-registry.ts +63 -0
- package/src/types.d.ts +4 -0
- package/src/widgets/ai-settings.tsx +1100 -0
- package/src/widgets/chat-wrapper.tsx +543 -0
- package/src/widgets/provider-config-dialog.tsx +256 -0
- package/style/base.css +335 -14
- package/style/icons/jupyternaut-lite.svg +1 -1
- package/lib/base-completer.d.ts +0 -49
- package/lib/base-completer.js +0 -14
- package/lib/chat-handler.d.ts +0 -56
- package/lib/chat-handler.js +0 -201
- package/lib/completion-provider.d.ts +0 -34
- package/lib/completion-provider.js +0 -32
- package/lib/default-prompts.d.ts +0 -2
- package/lib/default-prompts.js +0 -31
- package/lib/default-providers/Anthropic/completer.d.ts +0 -12
- package/lib/default-providers/Anthropic/completer.js +0 -46
- package/lib/default-providers/Anthropic/settings-schema.json +0 -70
- package/lib/default-providers/ChromeAI/completer.d.ts +0 -12
- package/lib/default-providers/ChromeAI/completer.js +0 -56
- package/lib/default-providers/ChromeAI/instructions.d.ts +0 -6
- package/lib/default-providers/ChromeAI/instructions.js +0 -42
- package/lib/default-providers/ChromeAI/settings-schema.json +0 -18
- package/lib/default-providers/Gemini/completer.d.ts +0 -12
- package/lib/default-providers/Gemini/completer.js +0 -48
- package/lib/default-providers/Gemini/instructions.d.ts +0 -2
- package/lib/default-providers/Gemini/instructions.js +0 -9
- package/lib/default-providers/Gemini/settings-schema.json +0 -64
- package/lib/default-providers/MistralAI/completer.d.ts +0 -13
- package/lib/default-providers/MistralAI/completer.js +0 -52
- package/lib/default-providers/MistralAI/instructions.d.ts +0 -2
- package/lib/default-providers/MistralAI/instructions.js +0 -18
- package/lib/default-providers/MistralAI/settings-schema.json +0 -75
- package/lib/default-providers/Ollama/completer.d.ts +0 -12
- package/lib/default-providers/Ollama/completer.js +0 -43
- package/lib/default-providers/Ollama/instructions.d.ts +0 -2
- package/lib/default-providers/Ollama/instructions.js +0 -70
- package/lib/default-providers/Ollama/settings-schema.json +0 -143
- package/lib/default-providers/OpenAI/completer.d.ts +0 -12
- package/lib/default-providers/OpenAI/completer.js +0 -43
- package/lib/default-providers/OpenAI/settings-schema.json +0 -628
- package/lib/default-providers/WebLLM/completer.d.ts +0 -21
- package/lib/default-providers/WebLLM/completer.js +0 -127
- package/lib/default-providers/WebLLM/instructions.d.ts +0 -6
- package/lib/default-providers/WebLLM/instructions.js +0 -32
- package/lib/default-providers/WebLLM/settings-schema.json +0 -19
- package/lib/default-providers/index.d.ts +0 -2
- package/lib/default-providers/index.js +0 -179
- package/lib/provider.d.ts +0 -144
- package/lib/provider.js +0 -412
- package/lib/settings/base.json +0 -7
- package/lib/settings/index.d.ts +0 -3
- package/lib/settings/index.js +0 -3
- package/lib/settings/panel.d.ts +0 -226
- package/lib/settings/panel.js +0 -510
- package/lib/settings/textarea.d.ts +0 -2
- package/lib/settings/textarea.js +0 -18
- package/lib/settings/utils.d.ts +0 -2
- package/lib/settings/utils.js +0 -4
- package/lib/types/ai-model.d.ts +0 -24
- package/lib/types/ai-model.js +0 -5
- package/schema/chat.json +0 -28
- package/schema/provider-registry.json +0 -29
- package/schema/system-prompts.json +0 -22
- package/src/base-completer.ts +0 -75
- package/src/chat-handler.ts +0 -262
- package/src/completion-provider.ts +0 -64
- package/src/default-prompts.ts +0 -33
- package/src/default-providers/Anthropic/completer.ts +0 -59
- package/src/default-providers/ChromeAI/completer.ts +0 -73
- package/src/default-providers/ChromeAI/instructions.ts +0 -45
- package/src/default-providers/Gemini/completer.ts +0 -61
- package/src/default-providers/Gemini/instructions.ts +0 -9
- package/src/default-providers/MistralAI/completer.ts +0 -69
- package/src/default-providers/MistralAI/instructions.ts +0 -18
- package/src/default-providers/Ollama/completer.ts +0 -54
- package/src/default-providers/Ollama/instructions.ts +0 -70
- package/src/default-providers/OpenAI/completer.ts +0 -54
- package/src/default-providers/WebLLM/completer.ts +0 -151
- package/src/default-providers/WebLLM/instructions.ts +0 -33
- package/src/default-providers/index.ts +0 -211
- package/src/global.d.ts +0 -9
- package/src/provider.ts +0 -514
- package/src/settings/index.ts +0 -3
- package/src/settings/panel.tsx +0 -773
- package/src/settings/textarea.tsx +0 -33
- package/src/settings/utils.ts +0 -5
- package/src/types/ai-model.ts +0 -37
- package/src/types/service-worker.d.ts +0 -6
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ISignal } from '@lumino/signaling';
|
|
2
|
+
import { ITool, IToolRegistry, INamedTool } from '../tokens';
|
|
3
|
+
/**
|
|
4
|
+
* Implementation of the tool registry for managing AI tools
|
|
5
|
+
*/
|
|
6
|
+
export declare class ToolRegistry implements IToolRegistry {
|
|
7
|
+
/**
|
|
8
|
+
* The registered tools as a record (name -> tool mapping).
|
|
9
|
+
*/
|
|
10
|
+
get tools(): Record<string, ITool>;
|
|
11
|
+
/**
|
|
12
|
+
* The registered named tools array.
|
|
13
|
+
*/
|
|
14
|
+
get namedTools(): INamedTool[];
|
|
15
|
+
/**
|
|
16
|
+
* A signal triggered when the tools have changed.
|
|
17
|
+
*/
|
|
18
|
+
get toolsChanged(): ISignal<IToolRegistry, void>;
|
|
19
|
+
/**
|
|
20
|
+
* Add a new tool to the registry.
|
|
21
|
+
*/
|
|
22
|
+
add(name: string, tool: ITool): void;
|
|
23
|
+
/**
|
|
24
|
+
* Get a tool for a given name.
|
|
25
|
+
* Return null if the name is not provided or if there is no registered tool with the
|
|
26
|
+
* given name.
|
|
27
|
+
*/
|
|
28
|
+
get(name: string | null): ITool | null;
|
|
29
|
+
/**
|
|
30
|
+
* Remove a tool from the registry by name.
|
|
31
|
+
*/
|
|
32
|
+
remove(name: string): boolean;
|
|
33
|
+
private _tools;
|
|
34
|
+
private _toolsChanged;
|
|
35
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Signal } from '@lumino/signaling';
|
|
2
|
+
/**
|
|
3
|
+
* Implementation of the tool registry for managing AI tools
|
|
4
|
+
*/
|
|
5
|
+
export class ToolRegistry {
|
|
6
|
+
/**
|
|
7
|
+
* The registered tools as a record (name -> tool mapping).
|
|
8
|
+
*/
|
|
9
|
+
get tools() {
|
|
10
|
+
return { ...this._tools }; // Return a copy to prevent external modification
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The registered named tools array.
|
|
14
|
+
*/
|
|
15
|
+
get namedTools() {
|
|
16
|
+
return Object.entries(this._tools).map(([name, tool]) => ({ name, tool }));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A signal triggered when the tools have changed.
|
|
20
|
+
*/
|
|
21
|
+
get toolsChanged() {
|
|
22
|
+
return this._toolsChanged;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Add a new tool to the registry.
|
|
26
|
+
*/
|
|
27
|
+
add(name, tool) {
|
|
28
|
+
this._tools[name] = tool;
|
|
29
|
+
this._toolsChanged.emit();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Get a tool for a given name.
|
|
33
|
+
* Return null if the name is not provided or if there is no registered tool with the
|
|
34
|
+
* given name.
|
|
35
|
+
*/
|
|
36
|
+
get(name) {
|
|
37
|
+
if (name === null) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return this._tools[name] || null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Remove a tool from the registry by name.
|
|
44
|
+
*/
|
|
45
|
+
remove(name) {
|
|
46
|
+
if (name in this._tools) {
|
|
47
|
+
delete this._tools[name];
|
|
48
|
+
this._toolsChanged.emit();
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
_tools = {};
|
|
54
|
+
_toolsChanged = new Signal(this);
|
|
55
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { IThemeManager } from '@jupyterlab/apputils';
|
|
2
|
+
import { ReactWidget } from '@jupyterlab/ui-components';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { AgentManager } from '../agent';
|
|
5
|
+
import { AISettingsModel } from '../models/settings-model';
|
|
6
|
+
import type { IChatProviderRegistry } from '../tokens';
|
|
7
|
+
/**
|
|
8
|
+
* A JupyterLab widget for AI settings configuration
|
|
9
|
+
*/
|
|
10
|
+
export declare class AISettingsWidget extends ReactWidget {
|
|
11
|
+
/**
|
|
12
|
+
* Construct a new AI settings widget
|
|
13
|
+
* @param options - The options for initializing the widget
|
|
14
|
+
*/
|
|
15
|
+
constructor(options: AISettingsWidget.IOptions);
|
|
16
|
+
/**
|
|
17
|
+
* Render the AI settings component
|
|
18
|
+
* @returns A React element containing the AI settings interface
|
|
19
|
+
*/
|
|
20
|
+
protected render(): React.ReactElement;
|
|
21
|
+
private _settingsModel;
|
|
22
|
+
private _agentManager?;
|
|
23
|
+
private _themeManager?;
|
|
24
|
+
private _chatProviderRegistry;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Namespace for AISettingsWidget types and interfaces
|
|
28
|
+
*/
|
|
29
|
+
export declare namespace AISettingsWidget {
|
|
30
|
+
/**
|
|
31
|
+
* Options interface for constructing an AISettingsWidget
|
|
32
|
+
*/
|
|
33
|
+
interface IOptions {
|
|
34
|
+
settingsModel: AISettingsModel;
|
|
35
|
+
agentManager?: AgentManager;
|
|
36
|
+
themeManager?: IThemeManager;
|
|
37
|
+
chatProviderRegistry: IChatProviderRegistry;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
import { ReactWidget } from '@jupyterlab/ui-components';
|
|
2
|
+
import Add from '@mui/icons-material/Add';
|
|
3
|
+
import Cable from '@mui/icons-material/Cable';
|
|
4
|
+
import CheckCircle from '@mui/icons-material/CheckCircle';
|
|
5
|
+
import CheckCircleOutline from '@mui/icons-material/CheckCircleOutline';
|
|
6
|
+
import Delete from '@mui/icons-material/Delete';
|
|
7
|
+
import Edit from '@mui/icons-material/Edit';
|
|
8
|
+
import Error from '@mui/icons-material/Error';
|
|
9
|
+
import ErrorOutline from '@mui/icons-material/ErrorOutline';
|
|
10
|
+
import MoreVert from '@mui/icons-material/MoreVert';
|
|
11
|
+
import Psychology from '@mui/icons-material/Psychology';
|
|
12
|
+
import Settings from '@mui/icons-material/Settings';
|
|
13
|
+
import { Alert, Box, Button, Card, CardContent, Dialog, DialogActions, DialogContent, DialogTitle, Divider, FormControl, FormControlLabel, IconButton, InputLabel, List, ListItem, ListItemSecondaryAction, ListItemText, Menu, MenuItem, Select, Slider, Switch, Tab, Tabs, TextField, ThemeProvider, Typography, createTheme } from '@mui/material';
|
|
14
|
+
import React, { useEffect, useState } from 'react';
|
|
15
|
+
import { ProviderConfigDialog } from './provider-config-dialog';
|
|
16
|
+
/**
|
|
17
|
+
* Create a theme that uses IThemeManager to detect theme
|
|
18
|
+
* @param themeManager - Optional theme manager to detect theme
|
|
19
|
+
* @returns A Material-UI theme configured for the current JupyterLab theme
|
|
20
|
+
*/
|
|
21
|
+
const createJupyterLabTheme = (themeManager) => {
|
|
22
|
+
// Use IThemeManager if available, otherwise default to light theme
|
|
23
|
+
const isDark = themeManager?.theme
|
|
24
|
+
? !themeManager.isLight(themeManager.theme)
|
|
25
|
+
: false;
|
|
26
|
+
return createTheme({
|
|
27
|
+
palette: {
|
|
28
|
+
mode: isDark ? 'dark' : 'light'
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* A JupyterLab widget for AI settings configuration
|
|
34
|
+
*/
|
|
35
|
+
export class AISettingsWidget extends ReactWidget {
|
|
36
|
+
/**
|
|
37
|
+
* Construct a new AI settings widget
|
|
38
|
+
* @param options - The options for initializing the widget
|
|
39
|
+
*/
|
|
40
|
+
constructor(options) {
|
|
41
|
+
super();
|
|
42
|
+
this._settingsModel = options.settingsModel;
|
|
43
|
+
this._agentManager = options.agentManager;
|
|
44
|
+
this._themeManager = options.themeManager;
|
|
45
|
+
this._chatProviderRegistry = options.chatProviderRegistry;
|
|
46
|
+
this.id = 'jupyterlite-ai-settings';
|
|
47
|
+
this.title.label = 'AI Settings';
|
|
48
|
+
this.title.caption = 'Configure AI providers and behavior';
|
|
49
|
+
this.title.closable = true;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Render the AI settings component
|
|
53
|
+
* @returns A React element containing the AI settings interface
|
|
54
|
+
*/
|
|
55
|
+
render() {
|
|
56
|
+
return (React.createElement(AISettingsComponent, { model: this._settingsModel, agentManager: this._agentManager, themeManager: this._themeManager, chatProviderRegistry: this._chatProviderRegistry }));
|
|
57
|
+
}
|
|
58
|
+
_settingsModel;
|
|
59
|
+
_agentManager;
|
|
60
|
+
_themeManager;
|
|
61
|
+
_chatProviderRegistry;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The main AI settings component that provides configuration UI
|
|
65
|
+
* @param props - Component props containing models and theme manager
|
|
66
|
+
* @returns A React component for AI settings configuration
|
|
67
|
+
*/
|
|
68
|
+
const AISettingsComponent = ({ model, agentManager, themeManager, chatProviderRegistry }) => {
|
|
69
|
+
if (!model) {
|
|
70
|
+
return React.createElement("div", null, "Settings model not available");
|
|
71
|
+
}
|
|
72
|
+
const [config, setConfig] = useState(model.config || {});
|
|
73
|
+
const [theme, setTheme] = useState(() => createJupyterLabTheme(themeManager));
|
|
74
|
+
const [activeTab, setActiveTab] = useState(0);
|
|
75
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
76
|
+
const [editingProvider, setEditingProvider] = useState();
|
|
77
|
+
const [menuAnchor, setMenuAnchor] = useState(null);
|
|
78
|
+
const [menuProviderId, setMenuProviderId] = useState('');
|
|
79
|
+
const [mcpDialogOpen, setMcpDialogOpen] = useState(false);
|
|
80
|
+
const [editingMCPServer, setEditingMCPServer] = useState();
|
|
81
|
+
const [mcpMenuAnchor, setMcpMenuAnchor] = useState(null);
|
|
82
|
+
const [mcpMenuServerId, setMcpMenuServerId] = useState('');
|
|
83
|
+
/**
|
|
84
|
+
* Effect to listen for model state changes and update config
|
|
85
|
+
*/
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!model || !model.stateChanged) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const onStateChanged = () => {
|
|
91
|
+
setConfig(model.config || {});
|
|
92
|
+
};
|
|
93
|
+
model.stateChanged.connect(onStateChanged);
|
|
94
|
+
return () => {
|
|
95
|
+
model.stateChanged.disconnect(onStateChanged);
|
|
96
|
+
};
|
|
97
|
+
}, [model]);
|
|
98
|
+
/**
|
|
99
|
+
* Effect to listen for theme changes and update the Material-UI theme
|
|
100
|
+
*/
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (!themeManager) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const updateTheme = () => {
|
|
106
|
+
setTheme(createJupyterLabTheme(themeManager));
|
|
107
|
+
};
|
|
108
|
+
themeManager.themeChanged.connect(updateTheme);
|
|
109
|
+
return () => {
|
|
110
|
+
themeManager.themeChanged.disconnect(updateTheme);
|
|
111
|
+
};
|
|
112
|
+
}, [themeManager]);
|
|
113
|
+
/**
|
|
114
|
+
* Effect to listen for MCP connection changes to re-render connection status
|
|
115
|
+
*/
|
|
116
|
+
useEffect(() => {
|
|
117
|
+
if (!agentManager) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const onMCPConnectionChanged = () => {
|
|
121
|
+
// Force a re-render by updating the config state
|
|
122
|
+
setConfig(prevConfig => ({ ...prevConfig }));
|
|
123
|
+
};
|
|
124
|
+
agentManager.mcpConnectionChanged.connect(onMCPConnectionChanged);
|
|
125
|
+
return () => {
|
|
126
|
+
agentManager.mcpConnectionChanged.disconnect(onMCPConnectionChanged);
|
|
127
|
+
};
|
|
128
|
+
}, [agentManager]);
|
|
129
|
+
/**
|
|
130
|
+
* Handle adding a new AI provider
|
|
131
|
+
* @param providerConfig - The provider configuration to add
|
|
132
|
+
*/
|
|
133
|
+
const handleAddProvider = async (providerConfig) => {
|
|
134
|
+
await model.addProvider(providerConfig);
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Handle editing an existing AI provider
|
|
138
|
+
* @param providerConfig - The updated provider configuration
|
|
139
|
+
*/
|
|
140
|
+
const handleEditProvider = async (providerConfig) => {
|
|
141
|
+
if (editingProvider) {
|
|
142
|
+
await model.updateProvider(editingProvider.id, providerConfig);
|
|
143
|
+
setEditingProvider(undefined);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
/**
|
|
147
|
+
* Handle deleting an AI provider
|
|
148
|
+
* @param id - The ID of the provider to delete
|
|
149
|
+
*/
|
|
150
|
+
const handleDeleteProvider = async (id) => {
|
|
151
|
+
await model.removeProvider(id);
|
|
152
|
+
setMenuAnchor(null);
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* Open the provider edit dialog
|
|
156
|
+
* @param provider - The provider to edit
|
|
157
|
+
*/
|
|
158
|
+
const openEditDialog = (provider) => {
|
|
159
|
+
setEditingProvider(provider);
|
|
160
|
+
setDialogOpen(true);
|
|
161
|
+
setMenuAnchor(null);
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Open the provider add dialog
|
|
165
|
+
*/
|
|
166
|
+
const openAddDialog = () => {
|
|
167
|
+
setEditingProvider(undefined);
|
|
168
|
+
setDialogOpen(true);
|
|
169
|
+
};
|
|
170
|
+
/**
|
|
171
|
+
* Handle provider menu click
|
|
172
|
+
* @param event - The click event
|
|
173
|
+
* @param providerId - The ID of the provider
|
|
174
|
+
*/
|
|
175
|
+
const handleMenuClick = (event, providerId) => {
|
|
176
|
+
setMenuAnchor(event.currentTarget);
|
|
177
|
+
setMenuProviderId(providerId);
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Handle provider menu close
|
|
181
|
+
*/
|
|
182
|
+
const handleMenuClose = () => {
|
|
183
|
+
setMenuAnchor(null);
|
|
184
|
+
setMenuProviderId('');
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Handle updating AI configuration
|
|
188
|
+
* @param updates - Partial configuration updates to apply
|
|
189
|
+
*/
|
|
190
|
+
const handleConfigUpdate = async (updates) => {
|
|
191
|
+
await model.updateConfig(updates);
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Handle adding a new MCP server
|
|
195
|
+
* @param serverConfig - The MCP server configuration to add
|
|
196
|
+
*/
|
|
197
|
+
const handleAddMCPServer = async (serverConfig) => {
|
|
198
|
+
await model.addMCPServer(serverConfig);
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Handle editing an existing MCP server
|
|
202
|
+
* @param serverConfig - The updated MCP server configuration
|
|
203
|
+
*/
|
|
204
|
+
const handleEditMCPServer = async (serverConfig) => {
|
|
205
|
+
if (editingMCPServer) {
|
|
206
|
+
await model.updateMCPServer(editingMCPServer.id, serverConfig);
|
|
207
|
+
setEditingMCPServer(undefined);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Handle deleting an MCP server
|
|
212
|
+
* @param id - The ID of the MCP server to delete
|
|
213
|
+
*/
|
|
214
|
+
const handleDeleteMCPServer = async (id) => {
|
|
215
|
+
await model.removeMCPServer(id);
|
|
216
|
+
setMcpMenuAnchor(null);
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* Open the MCP server edit dialog
|
|
220
|
+
* @param server - The MCP server to edit
|
|
221
|
+
*/
|
|
222
|
+
const openEditMCPDialog = (server) => {
|
|
223
|
+
setEditingMCPServer(server);
|
|
224
|
+
setMcpDialogOpen(true);
|
|
225
|
+
setMcpMenuAnchor(null);
|
|
226
|
+
};
|
|
227
|
+
/**
|
|
228
|
+
* Open the MCP server add dialog
|
|
229
|
+
*/
|
|
230
|
+
const openAddMCPDialog = () => {
|
|
231
|
+
setEditingMCPServer(undefined);
|
|
232
|
+
setMcpDialogOpen(true);
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* Handle MCP server menu click
|
|
236
|
+
* @param event - The click event
|
|
237
|
+
* @param serverId - The ID of the MCP server
|
|
238
|
+
*/
|
|
239
|
+
const handleMCPMenuClick = (event, serverId) => {
|
|
240
|
+
setMcpMenuAnchor(event.currentTarget);
|
|
241
|
+
setMcpMenuServerId(serverId);
|
|
242
|
+
};
|
|
243
|
+
/**
|
|
244
|
+
* Handle MCP server menu close
|
|
245
|
+
*/
|
|
246
|
+
const handleMCPMenuClose = () => {
|
|
247
|
+
setMcpMenuAnchor(null);
|
|
248
|
+
setMcpMenuServerId('');
|
|
249
|
+
};
|
|
250
|
+
const isValidConfig = agentManager?.hasValidConfig() ?? false;
|
|
251
|
+
return (React.createElement(ThemeProvider, { theme: theme },
|
|
252
|
+
React.createElement(Box, { sx: {
|
|
253
|
+
height: '100%',
|
|
254
|
+
maxHeight: '100vh',
|
|
255
|
+
overflow: 'auto',
|
|
256
|
+
p: 2,
|
|
257
|
+
pb: 4,
|
|
258
|
+
fontSize: '0.9rem'
|
|
259
|
+
} },
|
|
260
|
+
React.createElement(Box, { sx: { mb: 2, display: 'flex', alignItems: 'center', gap: 2 } },
|
|
261
|
+
React.createElement(Settings, { color: "primary", sx: { fontSize: 24 } }),
|
|
262
|
+
React.createElement(Typography, { variant: "h5", component: "h1", sx: { fontWeight: 600 } }, "AI Settings")),
|
|
263
|
+
React.createElement(Alert, { severity: isValidConfig ? 'success' : 'warning', icon: isValidConfig ? React.createElement(CheckCircle, null) : React.createElement(Error, null), sx: { mb: 2 } }, isValidConfig
|
|
264
|
+
? 'Configuration is valid and ready to use'
|
|
265
|
+
: 'Please add and configure a provider to get started'),
|
|
266
|
+
React.createElement(Box, { sx: { borderBottom: 1, borderColor: 'divider', mb: 2 } },
|
|
267
|
+
React.createElement(Tabs, { value: activeTab, onChange: (_, newValue) => setActiveTab(newValue) },
|
|
268
|
+
React.createElement(Tab, { label: "Providers" }),
|
|
269
|
+
React.createElement(Tab, { label: "Model Parameters" }),
|
|
270
|
+
React.createElement(Tab, { label: "Behavior" }),
|
|
271
|
+
React.createElement(Tab, { label: "MCP Servers" }))),
|
|
272
|
+
activeTab === 0 && (React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
|
|
273
|
+
config.providers.length > 0 && (React.createElement(Card, { elevation: 2 },
|
|
274
|
+
React.createElement(CardContent, null,
|
|
275
|
+
React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, "Active Providers"),
|
|
276
|
+
React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
|
|
277
|
+
React.createElement(FormControl, { fullWidth: true },
|
|
278
|
+
React.createElement(InputLabel, null, "Chat Provider"),
|
|
279
|
+
React.createElement(Select, { value: config.activeProvider, label: "Chat Provider", onChange: e => model.setActiveProvider(e.target.value) }, config.providers.map(provider => (React.createElement(MenuItem, { key: provider.id, value: provider.id }, provider.name))))),
|
|
280
|
+
React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.useSameProviderForChatAndCompleter, onChange: e => handleConfigUpdate({
|
|
281
|
+
useSameProviderForChatAndCompleter: e.target.checked
|
|
282
|
+
}), color: "primary" }), label: "Use same provider for chat and completions" }),
|
|
283
|
+
!config.useSameProviderForChatAndCompleter && (React.createElement(FormControl, { fullWidth: true },
|
|
284
|
+
React.createElement(InputLabel, null, "Completion Provider"),
|
|
285
|
+
React.createElement(Select, { value: config.activeCompleterProvider || '', label: "Completion Provider", onChange: e => model.setActiveCompleterProvider(e.target.value || undefined) },
|
|
286
|
+
React.createElement(MenuItem, { value: "" },
|
|
287
|
+
React.createElement("em", null, "Use chat provider")),
|
|
288
|
+
config.providers.map(provider => (React.createElement(MenuItem, { key: provider.id, value: provider.id }, provider.name)))))))))),
|
|
289
|
+
React.createElement(Card, { elevation: 2 },
|
|
290
|
+
React.createElement(CardContent, null,
|
|
291
|
+
React.createElement(Box, { sx: {
|
|
292
|
+
display: 'flex',
|
|
293
|
+
alignItems: 'center',
|
|
294
|
+
justifyContent: 'space-between',
|
|
295
|
+
mb: 2
|
|
296
|
+
} },
|
|
297
|
+
React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
|
|
298
|
+
React.createElement(Typography, { variant: "h6", component: "h2" }, "Configured Providers")),
|
|
299
|
+
React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddDialog, size: "small" }, "Add Provider")),
|
|
300
|
+
config.providers.length === 0 ? (React.createElement(Alert, { severity: "info" }, "No providers configured yet. Click \"Add Provider\" to get started.")) : (React.createElement(List, null, config.providers.map(provider => (React.createElement(ListItem, { key: provider.id, divider: true },
|
|
301
|
+
React.createElement(ListItemText, { primary: provider.name, secondary: React.createElement(Typography, { variant: "body2", color: "text.secondary" },
|
|
302
|
+
provider.provider,
|
|
303
|
+
" \u2022 ",
|
|
304
|
+
provider.model) }),
|
|
305
|
+
React.createElement(ListItemSecondaryAction, null,
|
|
306
|
+
React.createElement(IconButton, { onClick: e => handleMenuClick(e, provider.id), size: "small" },
|
|
307
|
+
React.createElement(MoreVert, null)))))))))))),
|
|
308
|
+
activeTab === 1 && (React.createElement(Card, { elevation: 2 },
|
|
309
|
+
React.createElement(CardContent, null,
|
|
310
|
+
React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', mb: 1.5, gap: 1 } },
|
|
311
|
+
React.createElement(Psychology, { color: "primary" }),
|
|
312
|
+
React.createElement(Typography, { variant: "h6", component: "h2" }, "Model Parameters")),
|
|
313
|
+
React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
|
|
314
|
+
React.createElement(Box, null,
|
|
315
|
+
React.createElement(Typography, { variant: "body1", gutterBottom: true },
|
|
316
|
+
"Temperature: ",
|
|
317
|
+
config.temperature),
|
|
318
|
+
React.createElement(Slider, { value: config.temperature, onChange: (_, value) => handleConfigUpdate({ temperature: value }), min: 0, max: 2, step: 0.1, valueLabelDisplay: "auto" }),
|
|
319
|
+
React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Controls response creativity: low value = deterministic and focused, middle value = balanced, high value = creative and varied")),
|
|
320
|
+
React.createElement(TextField, { fullWidth: true, label: "Max Tokens", type: "number", value: config.maxTokens || '', onChange: e => handleConfigUpdate({
|
|
321
|
+
maxTokens: e.target.value
|
|
322
|
+
? parseInt(e.target.value)
|
|
323
|
+
: undefined
|
|
324
|
+
}), inputProps: { min: 1, max: 8192 }, helperText: "Maximum length of AI responses (leave empty for provider default)" }),
|
|
325
|
+
React.createElement(TextField, { fullWidth: true, label: "Max Turns", type: "number", value: config.maxTurns, onChange: e => handleConfigUpdate({
|
|
326
|
+
maxTurns: parseInt(e.target.value)
|
|
327
|
+
}), inputProps: { min: 1, max: 100 }, helperText: "Maximum number of tool execution turns (when using tools)" }))))),
|
|
328
|
+
activeTab === 2 && (React.createElement(Card, { elevation: 2 },
|
|
329
|
+
React.createElement(CardContent, null,
|
|
330
|
+
React.createElement(Typography, { variant: "h6", component: "h2", gutterBottom: true }, "Behavior Settings"),
|
|
331
|
+
React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2 } },
|
|
332
|
+
React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.toolsEnabled, onChange: e => handleConfigUpdate({
|
|
333
|
+
toolsEnabled: e.target.checked
|
|
334
|
+
}), color: "primary" }), label: React.createElement(Box, null,
|
|
335
|
+
React.createElement(Typography, { variant: "body1" }, "Enable Tools"),
|
|
336
|
+
React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Allow the AI to use tools like notebook operations, code execution, and file management")) }),
|
|
337
|
+
React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.sendWithShiftEnter, onChange: e => handleConfigUpdate({
|
|
338
|
+
sendWithShiftEnter: e.target.checked
|
|
339
|
+
}), color: "primary" }), label: React.createElement(Box, null,
|
|
340
|
+
React.createElement(Typography, { variant: "body1" }, "Send with Shift+Enter"),
|
|
341
|
+
React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Use Shift+Enter to send messages (Enter creates new line)")) }),
|
|
342
|
+
React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: config.showTokenUsage, onChange: e => handleConfigUpdate({
|
|
343
|
+
showTokenUsage: e.target.checked
|
|
344
|
+
}), color: "primary" }), label: React.createElement(Box, null,
|
|
345
|
+
React.createElement(Typography, { variant: "body1" }, "Show Token Usage"),
|
|
346
|
+
React.createElement(Typography, { variant: "caption", color: "text.secondary" }, "Display token usage information in the chat toolbar")) }),
|
|
347
|
+
React.createElement(Divider, { sx: { my: 1 } }),
|
|
348
|
+
React.createElement(TextField, { fullWidth: true, multiline: true, rows: 3, label: "System Prompt", value: config.systemPrompt, onChange: e => handleConfigUpdate({ systemPrompt: e.target.value }), placeholder: "Define the AI's behavior and personality...", helperText: "Instructions that define how the AI should behave and respond" }),
|
|
349
|
+
React.createElement(Divider, { sx: { my: 2 } }),
|
|
350
|
+
React.createElement(Box, null,
|
|
351
|
+
React.createElement(Typography, { variant: "body1", gutterBottom: true }, "Commands Requiring Approval"),
|
|
352
|
+
React.createElement(Typography, { variant: "caption", color: "text.secondary", gutterBottom: true, sx: { display: 'block' } }, "Commands that require user approval before AI can execute them"),
|
|
353
|
+
React.createElement(List, { sx: { mb: 2, maxHeight: 200, overflow: 'auto' } }, config.commandsRequiringApproval.map((command, index) => (React.createElement(ListItem, { key: index, divider: true },
|
|
354
|
+
React.createElement(ListItemText, { primary: command }),
|
|
355
|
+
React.createElement(ListItemSecondaryAction, null,
|
|
356
|
+
React.createElement(IconButton, { onClick: () => {
|
|
357
|
+
const newCommands = [
|
|
358
|
+
...config.commandsRequiringApproval
|
|
359
|
+
];
|
|
360
|
+
newCommands.splice(index, 1);
|
|
361
|
+
handleConfigUpdate({
|
|
362
|
+
commandsRequiringApproval: newCommands
|
|
363
|
+
});
|
|
364
|
+
}, size: "small" },
|
|
365
|
+
React.createElement(Delete, null))))))),
|
|
366
|
+
React.createElement(TextField, { fullWidth: true, label: "Add New Command", placeholder: "e.g., notebook:run-cell", onKeyDown: e => {
|
|
367
|
+
if (e.key === 'Enter') {
|
|
368
|
+
const value = e.target.value.trim();
|
|
369
|
+
if (value &&
|
|
370
|
+
!config.commandsRequiringApproval.includes(value)) {
|
|
371
|
+
const newCommands = [
|
|
372
|
+
...config.commandsRequiringApproval,
|
|
373
|
+
value
|
|
374
|
+
];
|
|
375
|
+
handleConfigUpdate({
|
|
376
|
+
commandsRequiringApproval: newCommands
|
|
377
|
+
});
|
|
378
|
+
e.target.value = '';
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}, helperText: "Press Enter to add a command. Common commands: notebook:run-cell, console:execute, fileeditor:run-code" })))))),
|
|
382
|
+
activeTab === 3 && (React.createElement(Card, { elevation: 2 },
|
|
383
|
+
React.createElement(CardContent, null,
|
|
384
|
+
React.createElement(Box, { sx: {
|
|
385
|
+
display: 'flex',
|
|
386
|
+
alignItems: 'center',
|
|
387
|
+
justifyContent: 'space-between',
|
|
388
|
+
mb: 2
|
|
389
|
+
} },
|
|
390
|
+
React.createElement(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 } },
|
|
391
|
+
React.createElement(Cable, { color: "primary" }),
|
|
392
|
+
React.createElement(Typography, { variant: "h6", component: "h2" }, "Remote MCP Servers")),
|
|
393
|
+
React.createElement(Button, { variant: "contained", startIcon: React.createElement(Add, null), onClick: openAddMCPDialog, size: "small" }, "Add Server")),
|
|
394
|
+
React.createElement(Typography, { variant: "body2", color: "text.secondary", sx: { mb: 2 } }, "Configure remote Model Context Protocol (MCP) servers to extend the AI's capabilities with external tools and data sources."),
|
|
395
|
+
config.mcpServers.length === 0 ? (React.createElement(Alert, { severity: "info" }, "No MCP servers configured yet. Click \"Add Server\" to connect to remote MCP services.")) : (React.createElement(List, null, config.mcpServers.map(server => (React.createElement(ListItem, { key: server.id, divider: true },
|
|
396
|
+
React.createElement(ListItemText, { primary: React.createElement(Box, { sx: {
|
|
397
|
+
display: 'flex',
|
|
398
|
+
alignItems: 'center',
|
|
399
|
+
gap: 1
|
|
400
|
+
} },
|
|
401
|
+
React.createElement(Typography, { variant: "body1" }, server.name),
|
|
402
|
+
server.enabled &&
|
|
403
|
+
agentManager?.isMCPServerConnected(server.name) && (React.createElement(CheckCircleOutline, { sx: { color: 'success.main', fontSize: 16 } })),
|
|
404
|
+
server.enabled &&
|
|
405
|
+
!agentManager?.isMCPServerConnected(server.name) && (React.createElement(ErrorOutline, { sx: { color: 'error.main', fontSize: 16 } })),
|
|
406
|
+
React.createElement(Switch, { checked: server.enabled, onChange: e => model.updateMCPServer(server.id, {
|
|
407
|
+
enabled: e.target.checked
|
|
408
|
+
}), size: "small", color: "primary" })), secondary: React.createElement(Box, null,
|
|
409
|
+
React.createElement(Typography, { variant: "body2", color: "text.secondary" }, server.url),
|
|
410
|
+
server.enabled &&
|
|
411
|
+
server.connected !== undefined && (React.createElement(Typography, { variant: "caption", color: "text.secondary" },
|
|
412
|
+
"Status:",
|
|
413
|
+
' ',
|
|
414
|
+
server.connected
|
|
415
|
+
? 'Connected'
|
|
416
|
+
: 'Connection failed'))) }),
|
|
417
|
+
React.createElement(ListItemSecondaryAction, null,
|
|
418
|
+
React.createElement(IconButton, { onClick: e => handleMCPMenuClick(e, server.id), size: "small" },
|
|
419
|
+
React.createElement(MoreVert, null))))))))))),
|
|
420
|
+
React.createElement(ProviderConfigDialog, { open: dialogOpen, onClose: () => setDialogOpen(false), onSave: editingProvider ? handleEditProvider : handleAddProvider, initialConfig: editingProvider, mode: editingProvider ? 'edit' : 'add', chatProviderRegistry: chatProviderRegistry }),
|
|
421
|
+
React.createElement(Menu, { anchorEl: menuAnchor, open: Boolean(menuAnchor), onClose: handleMenuClose },
|
|
422
|
+
React.createElement(MenuItem, { onClick: () => {
|
|
423
|
+
const provider = config.providers.find(p => p.id === menuProviderId);
|
|
424
|
+
if (provider) {
|
|
425
|
+
openEditDialog(provider);
|
|
426
|
+
}
|
|
427
|
+
} },
|
|
428
|
+
React.createElement(Edit, { sx: { mr: 1 } }),
|
|
429
|
+
"Edit"),
|
|
430
|
+
React.createElement(MenuItem, { onClick: () => handleDeleteProvider(menuProviderId), sx: { color: 'error.main' } },
|
|
431
|
+
React.createElement(Delete, { sx: { mr: 1 } }),
|
|
432
|
+
"Delete")),
|
|
433
|
+
React.createElement(MCPServerDialog, { open: mcpDialogOpen, onClose: () => setMcpDialogOpen(false), onSave: editingMCPServer ? handleEditMCPServer : handleAddMCPServer, initialConfig: editingMCPServer, mode: editingMCPServer ? 'edit' : 'add' }),
|
|
434
|
+
React.createElement(Menu, { anchorEl: mcpMenuAnchor, open: Boolean(mcpMenuAnchor), onClose: handleMCPMenuClose },
|
|
435
|
+
React.createElement(MenuItem, { onClick: () => {
|
|
436
|
+
const server = config.mcpServers.find(s => s.id === mcpMenuServerId);
|
|
437
|
+
if (server) {
|
|
438
|
+
openEditMCPDialog(server);
|
|
439
|
+
}
|
|
440
|
+
} },
|
|
441
|
+
React.createElement(Edit, { sx: { mr: 1 } }),
|
|
442
|
+
"Edit"),
|
|
443
|
+
React.createElement(MenuItem, { onClick: () => handleDeleteMCPServer(mcpMenuServerId), sx: { color: 'error.main' } },
|
|
444
|
+
React.createElement(Delete, { sx: { mr: 1 } }),
|
|
445
|
+
"Delete")))));
|
|
446
|
+
};
|
|
447
|
+
/**
|
|
448
|
+
* Dialog component for adding/editing MCP server configurations
|
|
449
|
+
* @param props - Component props for the MCP server dialog
|
|
450
|
+
* @returns A React component for MCP server configuration
|
|
451
|
+
*/
|
|
452
|
+
const MCPServerDialog = ({ open, onClose, onSave, initialConfig, mode }) => {
|
|
453
|
+
const [name, setName] = useState(initialConfig?.name || '');
|
|
454
|
+
const [url, setUrl] = useState(initialConfig?.url || '');
|
|
455
|
+
const [enabled, setEnabled] = useState(initialConfig?.enabled ?? true);
|
|
456
|
+
/**
|
|
457
|
+
* Effect to reset dialog state when opened with new config
|
|
458
|
+
*/
|
|
459
|
+
useEffect(() => {
|
|
460
|
+
if (open) {
|
|
461
|
+
setName(initialConfig?.name || '');
|
|
462
|
+
setUrl(initialConfig?.url || '');
|
|
463
|
+
setEnabled(initialConfig?.enabled ?? true);
|
|
464
|
+
}
|
|
465
|
+
}, [open, initialConfig]);
|
|
466
|
+
/**
|
|
467
|
+
* Handle saving the MCP server configuration
|
|
468
|
+
*/
|
|
469
|
+
const handleSave = () => {
|
|
470
|
+
if (!name.trim() || !url.trim()) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
onSave({
|
|
474
|
+
name: name.trim(),
|
|
475
|
+
url: url.trim(),
|
|
476
|
+
enabled,
|
|
477
|
+
connected: false
|
|
478
|
+
});
|
|
479
|
+
onClose();
|
|
480
|
+
};
|
|
481
|
+
/**
|
|
482
|
+
* Check if a URL is valid
|
|
483
|
+
* @param url - The URL to validate
|
|
484
|
+
* @returns true if the URL is valid
|
|
485
|
+
*/
|
|
486
|
+
const _isValidUrl = (url) => {
|
|
487
|
+
try {
|
|
488
|
+
new URL(url);
|
|
489
|
+
return true;
|
|
490
|
+
}
|
|
491
|
+
catch {
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
const canSave = name.trim() && url.trim() && _isValidUrl(url.trim());
|
|
496
|
+
return (React.createElement(Dialog, { open: open, onClose: onClose, maxWidth: "sm", fullWidth: true },
|
|
497
|
+
React.createElement(DialogTitle, null, mode === 'add' ? 'Add MCP Server' : 'Edit MCP Server'),
|
|
498
|
+
React.createElement(DialogContent, null,
|
|
499
|
+
React.createElement(Box, { sx: { display: 'flex', flexDirection: 'column', gap: 2, pt: 1 } },
|
|
500
|
+
React.createElement(TextField, { autoFocus: true, fullWidth: true, label: "Server Name", value: name, onChange: e => setName(e.target.value), placeholder: "My MCP Server", helperText: "A friendly name to identify this MCP server" }),
|
|
501
|
+
React.createElement(TextField, { fullWidth: true, label: "Server URL", value: url, onChange: e => setUrl(e.target.value), placeholder: "https://example.com/mcp", helperText: "The HTTP/HTTPS URL of the MCP server", error: Boolean(url.trim() && !_isValidUrl(url.trim())) }),
|
|
502
|
+
React.createElement(FormControlLabel, { control: React.createElement(Switch, { checked: enabled, onChange: e => setEnabled(e.target.checked), color: "primary" }), label: "Enable this server" }))),
|
|
503
|
+
React.createElement(DialogActions, null,
|
|
504
|
+
React.createElement(Button, { onClick: onClose }, "Cancel"),
|
|
505
|
+
React.createElement(Button, { onClick: handleSave, variant: "contained", disabled: !canSave }, mode === 'add' ? 'Add' : 'Save'))));
|
|
506
|
+
};
|