@notebook-intelligence/notebook-intelligence 2.4.2 → 2.6.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.
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare function CheckBoxItem(props: any): React.JSX.Element;
@@ -0,0 +1,11 @@
1
+ // Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
2
+ import React from 'react';
3
+ import { MdOutlineCheckBoxOutlineBlank, MdCheckBox } from 'react-icons/md';
4
+ export function CheckBoxItem(props) {
5
+ const indent = props.indent || 0;
6
+ return (React.createElement("div", { className: `checkbox-item checkbox-item-indent-${indent} ${props.header ? 'checkbox-item-header' : ''}`, title: props.title, onClick: event => props.onClick(event) },
7
+ React.createElement("div", { className: "checkbox-item-toggle" },
8
+ props.checked ? (React.createElement(MdCheckBox, { className: "checkbox-icon" })) : (React.createElement(MdOutlineCheckBoxOutlineBlank, { className: "checkbox-icon" })),
9
+ props.label),
10
+ props.title && (React.createElement("div", { className: "checkbox-item-description" }, props.title))));
11
+ }
@@ -0,0 +1,2 @@
1
+ export declare function mcpServerSettingsToEnabledState(mcpServers: any, mcpServerSettings: any): Map<string, Set<string>>;
2
+ export declare function mcpServerSettingsToServerToolEnabledState(mcpServers: any, mcpServerSettings: any, serverId: string): Set<string>;
@@ -0,0 +1,37 @@
1
+ // Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
2
+ export function mcpServerSettingsToEnabledState(mcpServers, mcpServerSettings) {
3
+ const mcpServerEnabledState = new Map();
4
+ for (const server of mcpServers) {
5
+ const mcpServerToolEnabledState = mcpServerSettingsToServerToolEnabledState(mcpServers, mcpServerSettings, server.id);
6
+ if (mcpServerToolEnabledState) {
7
+ mcpServerEnabledState.set(server.id, mcpServerToolEnabledState);
8
+ }
9
+ }
10
+ return mcpServerEnabledState;
11
+ }
12
+ export function mcpServerSettingsToServerToolEnabledState(mcpServers, mcpServerSettings, serverId) {
13
+ var _a;
14
+ const server = mcpServers.find((server) => server.id === serverId);
15
+ let mcpServerToolEnabledState = null;
16
+ if (!server) {
17
+ return mcpServerToolEnabledState;
18
+ }
19
+ if (mcpServerSettings[server.id]) {
20
+ const serverSettings = mcpServerSettings[server.id];
21
+ if (!serverSettings.disabled) {
22
+ mcpServerToolEnabledState = new Set();
23
+ for (const tool of server.tools) {
24
+ if (!((_a = serverSettings.disabled_tools) === null || _a === void 0 ? void 0 : _a.includes(tool.name))) {
25
+ mcpServerToolEnabledState.add(tool.name);
26
+ }
27
+ }
28
+ }
29
+ }
30
+ else {
31
+ mcpServerToolEnabledState = new Set();
32
+ for (const tool of server.tools) {
33
+ mcpServerToolEnabledState.add(tool.name);
34
+ }
35
+ }
36
+ return mcpServerToolEnabledState;
37
+ }
@@ -0,0 +1,2 @@
1
+ import React from 'react';
2
+ export declare function PillItem(props: any): React.JSX.Element;
@@ -0,0 +1,5 @@
1
+ // Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
2
+ import React from 'react';
3
+ export function PillItem(props) {
4
+ return (React.createElement("div", { className: `pill-item ${props.checked ? 'checked' : ''}`, title: props.title, onClick: event => props.onClick(event) }, props.label));
5
+ }
@@ -0,0 +1,11 @@
1
+ /// <reference types="react" />
2
+ import { ReactWidget } from '@jupyterlab/apputils';
3
+ export declare class SettingsPanel extends ReactWidget {
4
+ constructor(options: {
5
+ onSave: () => void;
6
+ onEditMCPConfigClicked: () => void;
7
+ });
8
+ render(): JSX.Element;
9
+ private _onSave;
10
+ private _onEditMCPConfigClicked;
11
+ }
@@ -0,0 +1,374 @@
1
+ // Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
2
+ import React, { useEffect, useRef, useState } from 'react';
3
+ import { ReactWidget } from '@jupyterlab/apputils';
4
+ import { VscWarning } from 'react-icons/vsc';
5
+ import * as path from 'path';
6
+ import copySvgstr from '../../style/icons/copy.svg';
7
+ import { NBIAPI } from '../api';
8
+ import { CheckBoxItem } from './checkbox';
9
+ import { PillItem } from './pill';
10
+ import { mcpServerSettingsToEnabledState } from './mcp-util';
11
+ const OPENAI_COMPATIBLE_CHAT_MODEL_ID = 'openai-compatible-chat-model';
12
+ const LITELLM_COMPATIBLE_CHAT_MODEL_ID = 'litellm-compatible-chat-model';
13
+ const OPENAI_COMPATIBLE_INLINE_COMPLETION_MODEL_ID = 'openai-compatible-inline-completion-model';
14
+ const LITELLM_COMPATIBLE_INLINE_COMPLETION_MODEL_ID = 'litellm-compatible-inline-completion-model';
15
+ export class SettingsPanel extends ReactWidget {
16
+ constructor(options) {
17
+ super();
18
+ this._onSave = options.onSave;
19
+ this._onEditMCPConfigClicked = options.onEditMCPConfigClicked;
20
+ }
21
+ render() {
22
+ return (React.createElement(SettingsPanelComponent, { onSave: this._onSave, onEditMCPConfigClicked: this._onEditMCPConfigClicked }));
23
+ }
24
+ }
25
+ function SettingsPanelComponent(props) {
26
+ const [activeTab, setActiveTab] = useState('general');
27
+ const onTabSelected = (tab) => {
28
+ setActiveTab(tab);
29
+ };
30
+ return (React.createElement("div", { className: "nbi-settings-panel" },
31
+ React.createElement("div", { className: "nbi-settings-panel-tabs" },
32
+ React.createElement(SettingsPanelTabsComponent, { onTabSelected: onTabSelected, activeTab: activeTab })),
33
+ React.createElement("div", { className: "nbi-settings-panel-tab-content" },
34
+ activeTab === 'general' && (React.createElement(SettingsPanelComponentGeneral, { onSave: props.onSave, onEditMCPConfigClicked: props.onEditMCPConfigClicked })),
35
+ activeTab === 'mcp-servers' && (React.createElement(SettingsPanelComponentMCPServers, { onEditMCPConfigClicked: props.onEditMCPConfigClicked })))));
36
+ }
37
+ function SettingsPanelTabsComponent(props) {
38
+ const [activeTab, setActiveTab] = useState(props.activeTab);
39
+ return (React.createElement("div", null,
40
+ React.createElement("div", { className: `nbi-settings-panel-tab ${activeTab === 'general' ? 'active' : ''}`, onClick: () => {
41
+ setActiveTab('general');
42
+ props.onTabSelected('general');
43
+ } }, "General"),
44
+ React.createElement("div", { className: `nbi-settings-panel-tab ${activeTab === 'mcp-servers' ? 'active' : ''}`, onClick: () => {
45
+ setActiveTab('mcp-servers');
46
+ props.onTabSelected('mcp-servers');
47
+ } }, "MCP Servers")));
48
+ }
49
+ function SettingsPanelComponentGeneral(props) {
50
+ const nbiConfig = NBIAPI.config;
51
+ const llmProviders = nbiConfig.llmProviders;
52
+ const [chatModels, setChatModels] = useState([]);
53
+ const [inlineCompletionModels, setInlineCompletionModels] = useState([]);
54
+ const handleSaveSettings = async () => {
55
+ const config = {
56
+ default_chat_mode: defaultChatMode,
57
+ chat_model: {
58
+ provider: chatModelProvider,
59
+ model: chatModel,
60
+ properties: chatModelProperties
61
+ },
62
+ inline_completion_model: {
63
+ provider: inlineCompletionModelProvider,
64
+ model: inlineCompletionModel,
65
+ properties: inlineCompletionModelProperties
66
+ }
67
+ };
68
+ if (chatModelProvider === 'github-copilot' ||
69
+ inlineCompletionModelProvider === 'github-copilot') {
70
+ config.store_github_access_token = storeGitHubAccessToken;
71
+ }
72
+ await NBIAPI.setConfig(config);
73
+ props.onSave();
74
+ };
75
+ const handleRefreshOllamaModelListClick = async () => {
76
+ await NBIAPI.updateOllamaModelList();
77
+ updateModelOptionsForProvider(chatModelProvider, 'chat');
78
+ };
79
+ const [chatModelProvider, setChatModelProvider] = useState(nbiConfig.chatModel.provider || 'none');
80
+ const [inlineCompletionModelProvider, setInlineCompletionModelProvider] = useState(nbiConfig.inlineCompletionModel.provider || 'none');
81
+ const [defaultChatMode, setDefaultChatMode] = useState(nbiConfig.defaultChatMode);
82
+ const [chatModel, setChatModel] = useState(nbiConfig.chatModel.model);
83
+ const [chatModelProperties, setChatModelProperties] = useState([]);
84
+ const [inlineCompletionModelProperties, setInlineCompletionModelProperties] = useState([]);
85
+ const [inlineCompletionModel, setInlineCompletionModel] = useState(nbiConfig.inlineCompletionModel.model);
86
+ const [storeGitHubAccessToken, setStoreGitHubAccessToken] = useState(nbiConfig.storeGitHubAccessToken);
87
+ const updateModelOptionsForProvider = (providerId, modelType) => {
88
+ if (modelType === 'chat') {
89
+ setChatModelProvider(providerId);
90
+ }
91
+ else {
92
+ setInlineCompletionModelProvider(providerId);
93
+ }
94
+ const models = modelType === 'chat'
95
+ ? nbiConfig.chatModels
96
+ : nbiConfig.inlineCompletionModels;
97
+ const selectedModelId = modelType === 'chat'
98
+ ? nbiConfig.chatModel.model
99
+ : nbiConfig.inlineCompletionModel.model;
100
+ const providerModels = models.filter((model) => model.provider === providerId);
101
+ if (modelType === 'chat') {
102
+ setChatModels(providerModels);
103
+ }
104
+ else {
105
+ setInlineCompletionModels(providerModels);
106
+ }
107
+ let selectedModel = providerModels.find((model) => model.id === selectedModelId);
108
+ if (!selectedModel) {
109
+ selectedModel = providerModels === null || providerModels === void 0 ? void 0 : providerModels[0];
110
+ }
111
+ if (selectedModel) {
112
+ if (modelType === 'chat') {
113
+ setChatModel(selectedModel.id);
114
+ setChatModelProperties(selectedModel.properties);
115
+ }
116
+ else {
117
+ setInlineCompletionModel(selectedModel.id);
118
+ setInlineCompletionModelProperties(selectedModel.properties);
119
+ }
120
+ }
121
+ else {
122
+ if (modelType === 'chat') {
123
+ setChatModelProperties([]);
124
+ }
125
+ else {
126
+ setInlineCompletionModelProperties([]);
127
+ }
128
+ }
129
+ };
130
+ const onModelPropertyChange = (modelType, propertyId, value) => {
131
+ const modelProperties = modelType === 'chat'
132
+ ? chatModelProperties
133
+ : inlineCompletionModelProperties;
134
+ const updatedProperties = modelProperties.map((property) => {
135
+ if (property.id === propertyId) {
136
+ return { ...property, value };
137
+ }
138
+ return property;
139
+ });
140
+ if (modelType === 'chat') {
141
+ setChatModelProperties(updatedProperties);
142
+ }
143
+ else {
144
+ setInlineCompletionModelProperties(updatedProperties);
145
+ }
146
+ };
147
+ useEffect(() => {
148
+ updateModelOptionsForProvider(chatModelProvider, 'chat');
149
+ updateModelOptionsForProvider(inlineCompletionModelProvider, 'inline-completion');
150
+ }, []);
151
+ useEffect(() => {
152
+ handleSaveSettings();
153
+ }, [
154
+ defaultChatMode,
155
+ chatModelProvider,
156
+ chatModel,
157
+ chatModelProperties,
158
+ inlineCompletionModelProvider,
159
+ inlineCompletionModel,
160
+ inlineCompletionModelProperties,
161
+ storeGitHubAccessToken
162
+ ]);
163
+ return (React.createElement("div", { className: "config-dialog" },
164
+ React.createElement("div", { className: "config-dialog-body" },
165
+ React.createElement("div", { className: "model-config-section" },
166
+ React.createElement("div", { className: "model-config-section-header" }, "Default chat mode"),
167
+ React.createElement("div", { className: "model-config-section-body" },
168
+ React.createElement("div", { className: "model-config-section-row" },
169
+ React.createElement("div", { className: "model-config-section-column" },
170
+ React.createElement("div", null,
171
+ React.createElement("select", { className: "jp-mod-styled", value: defaultChatMode, onChange: event => setDefaultChatMode(event.target.value) },
172
+ React.createElement("option", { value: "ask" }, "Ask"),
173
+ React.createElement("option", { value: "agent" }, "Agent")))),
174
+ React.createElement("div", { className: "model-config-section-column" }, " ")))),
175
+ React.createElement("div", { className: "model-config-section" },
176
+ React.createElement("div", { className: "model-config-section-header" }, "Chat model"),
177
+ React.createElement("div", { className: "model-config-section-body" },
178
+ React.createElement("div", { className: "model-config-section-row" },
179
+ React.createElement("div", { className: "model-config-section-column" },
180
+ React.createElement("div", null, "Provider"),
181
+ React.createElement("div", null,
182
+ React.createElement("select", { className: "jp-mod-styled", onChange: event => updateModelOptionsForProvider(event.target.value, 'chat') },
183
+ llmProviders.map((provider, index) => (React.createElement("option", { key: index, value: provider.id, selected: provider.id === chatModelProvider }, provider.name))),
184
+ React.createElement("option", { key: -1, value: "none", selected: chatModelProvider === 'none' ||
185
+ !llmProviders.find(provider => provider.id === chatModelProvider) }, "None")))),
186
+ !['openai-compatible', 'litellm-compatible', 'none'].includes(chatModelProvider) &&
187
+ chatModels.length > 0 && (React.createElement("div", { className: "model-config-section-column" },
188
+ React.createElement("div", null, "Model"),
189
+ ![
190
+ OPENAI_COMPATIBLE_CHAT_MODEL_ID,
191
+ LITELLM_COMPATIBLE_CHAT_MODEL_ID
192
+ ].includes(chatModel) &&
193
+ chatModels.length > 0 && (React.createElement("div", null,
194
+ React.createElement("select", { className: "jp-mod-styled", onChange: event => setChatModel(event.target.value) }, chatModels.map((model, index) => (React.createElement("option", { key: index, value: model.id, selected: model.id === chatModel }, model.name))))))))),
195
+ React.createElement("div", { className: "model-config-section-row" },
196
+ React.createElement("div", { className: "model-config-section-column" }, chatModelProvider === 'ollama' && chatModels.length === 0 && (React.createElement("div", { className: "ollama-warning-message" },
197
+ "No Ollama models found! Make sure",
198
+ ' ',
199
+ React.createElement("a", { href: "https://ollama.com/", target: "_blank" }, "Ollama"),
200
+ ' ',
201
+ "is running and models are downloaded to your computer.",
202
+ ' ',
203
+ React.createElement("a", { href: "javascript:void(0)", onClick: handleRefreshOllamaModelListClick }, "Try again"),
204
+ ' ',
205
+ "once ready.")))),
206
+ React.createElement("div", { className: "model-config-section-row" },
207
+ React.createElement("div", { className: "model-config-section-column" }, chatModelProperties.map((property, index) => (React.createElement("div", { className: "form-field-row", key: index },
208
+ React.createElement("div", { className: "form-field-description" },
209
+ property.name,
210
+ " ",
211
+ property.optional ? '(optional)' : ''),
212
+ React.createElement("input", { name: "chat-model-id-input", placeholder: property.description, className: "jp-mod-styled", spellCheck: false, value: property.value, onChange: event => onModelPropertyChange('chat', property.id, event.target.value) })))))))),
213
+ React.createElement("div", { className: "model-config-section" },
214
+ React.createElement("div", { className: "model-config-section-header" }, "Auto-complete model"),
215
+ React.createElement("div", { className: "model-config-section-body" },
216
+ React.createElement("div", { className: "model-config-section-row" },
217
+ React.createElement("div", { className: "model-config-section-column" },
218
+ React.createElement("div", null, "Provider"),
219
+ React.createElement("div", null,
220
+ React.createElement("select", { className: "jp-mod-styled", onChange: event => updateModelOptionsForProvider(event.target.value, 'inline-completion') },
221
+ llmProviders.map((provider, index) => (React.createElement("option", { key: index, value: provider.id, selected: provider.id === inlineCompletionModelProvider }, provider.name))),
222
+ React.createElement("option", { key: -1, value: "none", selected: inlineCompletionModelProvider === 'none' ||
223
+ !llmProviders.find(provider => provider.id === inlineCompletionModelProvider) }, "None")))),
224
+ !['openai-compatible', 'litellm-compatible', 'none'].includes(inlineCompletionModelProvider) && (React.createElement("div", { className: "model-config-section-column" },
225
+ React.createElement("div", null, "Model"),
226
+ ![
227
+ OPENAI_COMPATIBLE_INLINE_COMPLETION_MODEL_ID,
228
+ LITELLM_COMPATIBLE_INLINE_COMPLETION_MODEL_ID
229
+ ].includes(inlineCompletionModel) && (React.createElement("div", null,
230
+ React.createElement("select", { className: "jp-mod-styled", onChange: event => setInlineCompletionModel(event.target.value) }, inlineCompletionModels.map((model, index) => (React.createElement("option", { key: index, value: model.id, selected: model.id === inlineCompletionModel }, model.name))))))))),
231
+ React.createElement("div", { className: "model-config-section-row" },
232
+ React.createElement("div", { className: "model-config-section-column" }, inlineCompletionModelProperties.map((property, index) => (React.createElement("div", { className: "form-field-row", key: index },
233
+ React.createElement("div", { className: "form-field-description" },
234
+ property.name,
235
+ " ",
236
+ property.optional ? '(optional)' : ''),
237
+ React.createElement("input", { name: "inline-completion-model-id-input", placeholder: property.description, className: "jp-mod-styled", spellCheck: false, value: property.value, onChange: event => onModelPropertyChange('inline-completion', property.id, event.target.value) })))))))),
238
+ (chatModelProvider === 'github-copilot' ||
239
+ inlineCompletionModelProvider === 'github-copilot') && (React.createElement("div", { className: "model-config-section" },
240
+ React.createElement("div", { className: "model-config-section-header access-token-config-header" },
241
+ "GitHub Copilot login",
242
+ ' ',
243
+ React.createElement("a", { href: "https://github.com/notebook-intelligence/notebook-intelligence/blob/main/README.md#remembering-github-copilot-login", target: "_blank" },
244
+ ' ',
245
+ React.createElement(VscWarning, { className: "access-token-warning", title: "Click to learn more about security implications" }))),
246
+ React.createElement("div", { className: "model-config-section-body" },
247
+ React.createElement("div", { className: "model-config-section-row" },
248
+ React.createElement("div", { className: "model-config-section-column" },
249
+ React.createElement("label", null,
250
+ React.createElement("input", { type: "checkbox", checked: storeGitHubAccessToken, onChange: event => {
251
+ setStoreGitHubAccessToken(event.target.checked);
252
+ } }),
253
+ "Remember my GitHub Copilot access token")))))),
254
+ React.createElement("div", { className: "model-config-section" },
255
+ React.createElement("div", { className: "model-config-section-header" }, "Config file path"),
256
+ React.createElement("div", { className: "model-config-section-body" },
257
+ React.createElement("div", { className: "model-config-section-row" },
258
+ React.createElement("div", { className: "model-config-section-column" },
259
+ React.createElement("span", { className: "user-code-span", onClick: () => {
260
+ navigator.clipboard.writeText(path.join(NBIAPI.config.userConfigDir, 'config.json'));
261
+ return true;
262
+ } },
263
+ path.join(NBIAPI.config.userConfigDir, 'config.json'),
264
+ ' ',
265
+ React.createElement("span", { className: "copy-icon", dangerouslySetInnerHTML: { __html: copySvgstr } })))))))));
266
+ }
267
+ function SettingsPanelComponentMCPServers(props) {
268
+ const nbiConfig = NBIAPI.config;
269
+ const mcpServersRef = useRef(nbiConfig.toolConfig.mcpServers);
270
+ const mcpServerSettingsRef = useRef(nbiConfig.mcpServerSettings);
271
+ const [renderCount, setRenderCount] = useState(1);
272
+ const [mcpServerEnabledState, setMCPServerEnabledState] = useState(new Map(mcpServerSettingsToEnabledState(mcpServersRef.current, mcpServerSettingsRef.current)));
273
+ const mcpServerEnabledStateToMcpServerSettings = () => {
274
+ const mcpServerSettings = {};
275
+ for (const mcpServer of mcpServersRef.current) {
276
+ if (mcpServerEnabledState.has(mcpServer.id)) {
277
+ const disabledTools = [];
278
+ for (const tool of mcpServer.tools) {
279
+ if (!mcpServerEnabledState.get(mcpServer.id).has(tool.name)) {
280
+ disabledTools.push(tool.name);
281
+ }
282
+ }
283
+ mcpServerSettings[mcpServer.id] = {
284
+ disabled: false,
285
+ disabled_tools: disabledTools
286
+ };
287
+ }
288
+ else {
289
+ mcpServerSettings[mcpServer.id] = { disabled: true };
290
+ }
291
+ }
292
+ return mcpServerSettings;
293
+ };
294
+ const syncSettingsToServerState = () => {
295
+ NBIAPI.setConfig({
296
+ mcp_server_settings: mcpServerSettingsRef.current
297
+ });
298
+ };
299
+ const handleReloadMCPServersClick = async () => {
300
+ await NBIAPI.reloadMCPServers();
301
+ };
302
+ useEffect(() => {
303
+ syncSettingsToServerState();
304
+ }, [mcpServerSettingsRef.current]);
305
+ useEffect(() => {
306
+ mcpServerSettingsRef.current = mcpServerEnabledStateToMcpServerSettings();
307
+ setRenderCount(renderCount => renderCount + 1);
308
+ }, [mcpServerEnabledState]);
309
+ const setMCPServerEnabled = (serverId, enabled) => {
310
+ var _a;
311
+ const currentState = new Map(mcpServerEnabledState);
312
+ if (enabled) {
313
+ if (!(serverId in currentState)) {
314
+ currentState.set(serverId, new Set((_a = mcpServersRef.current
315
+ .find((server) => server.id === serverId)) === null || _a === void 0 ? void 0 : _a.tools.map((tool) => tool.name)));
316
+ }
317
+ }
318
+ else {
319
+ currentState.delete(serverId);
320
+ }
321
+ setMCPServerEnabledState(currentState);
322
+ };
323
+ const getMCPServerEnabled = (serverId) => {
324
+ return mcpServerEnabledState.has(serverId);
325
+ };
326
+ const getMCPServerToolEnabled = (serverId, toolName) => {
327
+ return (mcpServerEnabledState.has(serverId) &&
328
+ mcpServerEnabledState.get(serverId).has(toolName));
329
+ };
330
+ const setMCPServerToolEnabled = (serverId, toolName, enabled) => {
331
+ const currentState = new Map(mcpServerEnabledState);
332
+ const serverState = currentState.get(serverId);
333
+ if (enabled) {
334
+ serverState.add(toolName);
335
+ }
336
+ else {
337
+ serverState.delete(toolName);
338
+ }
339
+ setMCPServerEnabledState(currentState);
340
+ };
341
+ useEffect(() => {
342
+ NBIAPI.configChanged.connect(() => {
343
+ mcpServersRef.current = nbiConfig.toolConfig.mcpServers;
344
+ mcpServerSettingsRef.current = nbiConfig.mcpServerSettings;
345
+ setRenderCount(renderCount => renderCount + 1);
346
+ });
347
+ }, []);
348
+ return (React.createElement("div", { className: "config-dialog" },
349
+ React.createElement("div", { className: "config-dialog-body" },
350
+ React.createElement("div", { className: "model-config-section" },
351
+ React.createElement("div", { className: "model-config-section-header", style: { display: 'flex' } },
352
+ React.createElement("div", { style: { flexGrow: 1 } }, "MCP Servers"),
353
+ React.createElement("div", null,
354
+ React.createElement("button", { className: "jp-toast-button jp-mod-small jp-Button", onClick: handleReloadMCPServersClick },
355
+ React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Reload")))),
356
+ React.createElement("div", { className: "model-config-section-body" },
357
+ mcpServersRef.current.length === 0 && renderCount > 0 && (React.createElement("div", { className: "model-config-section-row" },
358
+ React.createElement("div", { className: "model-config-section-column" },
359
+ React.createElement("div", null, "No MCP servers found. Add MCP servers in the configuration file.")))),
360
+ mcpServersRef.current.length > 0 && renderCount > 0 && (React.createElement("div", { className: "model-config-section-row" },
361
+ React.createElement("div", { className: "model-config-section-column" }, mcpServersRef.current.map((server) => (React.createElement("div", { key: server.id },
362
+ React.createElement("div", { style: { display: 'flex', alignItems: 'center' } },
363
+ React.createElement(CheckBoxItem, { header: true, label: server.id, checked: getMCPServerEnabled(server.id), onClick: () => {
364
+ setMCPServerEnabled(server.id, !getMCPServerEnabled(server.id));
365
+ } }),
366
+ React.createElement("div", { className: `server-status-indicator ${server.status}`, title: server.status })),
367
+ getMCPServerEnabled(server.id) && (React.createElement("div", null, server.tools.map((tool) => (React.createElement(PillItem, { label: tool.name, title: tool.description, checked: getMCPServerToolEnabled(server.id, tool.name), onClick: () => {
368
+ setMCPServerToolEnabled(server.id, tool.name, !getMCPServerToolEnabled(server.id, tool.name));
369
+ } }))))))))))),
370
+ React.createElement("div", { className: "model-config-section-row" },
371
+ React.createElement("div", { className: "model-config-section-column", style: { flexGrow: 'initial' } },
372
+ React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", style: { width: 'max-content' }, onClick: props.onEditMCPConfigClicked },
373
+ React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Add / Edit")))))))));
374
+ }
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
2
2
  import { IDocumentManager } from '@jupyterlab/docmanager';
3
- import { Dialog, ICommandPalette } from '@jupyterlab/apputils';
3
+ import { Dialog, ICommandPalette, MainAreaWidget } from '@jupyterlab/apputils';
4
4
  import { IMainMenu } from '@jupyterlab/mainmenu';
5
5
  import { IEditorLanguageRegistry } from '@jupyterlab/codemirror';
6
6
  import { CodeCell } from '@jupyterlab/cells';
@@ -14,14 +14,16 @@ import { LabIcon } from '@jupyterlab/ui-components';
14
14
  import { Menu, Panel, Widget } from '@lumino/widgets';
15
15
  import { CommandRegistry } from '@lumino/commands';
16
16
  import { IStatusBar } from '@jupyterlab/statusbar';
17
- import { ChatSidebar, ConfigurationDialogBody, GitHubCopilotLoginDialogBody, GitHubCopilotStatusBarItem, InlinePromptWidget, RunChatCompletionType } from './chat-sidebar';
17
+ import { ChatSidebar, FormInputDialogBody, GitHubCopilotLoginDialogBody, GitHubCopilotStatusBarItem, InlinePromptWidget, RunChatCompletionType } from './chat-sidebar';
18
18
  import { NBIAPI, GitHubCopilotLoginStatus } from './api';
19
19
  import { BackendMessageType, GITHUB_COPILOT_PROVIDER_ID, INotebookIntelligence, RequestDataType, TelemetryEventType } from './tokens';
20
20
  import sparklesSvgstr from '../style/icons/sparkles.svg';
21
21
  import copilotSvgstr from '../style/icons/copilot.svg';
22
+ import sparklesWarningSvgstr from '../style/icons/sparkles-warning.svg';
22
23
  import { applyCodeToSelectionInEditor, cellOutputAsText, compareSelections, extractLLMGeneratedCode, getSelectionInEditor, getTokenCount, getWholeNotebookContent, isSelectionEmpty, markdownToComment, waitForDuration } from './utils';
23
24
  import { UUID } from '@lumino/coreutils';
24
25
  import * as path from 'path';
26
+ import { SettingsPanel } from './components/settings-panel';
25
27
  var CommandIDs;
26
28
  (function (CommandIDs) {
27
29
  CommandIDs.chatuserInput = 'notebook-intelligence:chat-user-input';
@@ -51,6 +53,7 @@ var CommandIDs;
51
53
  CommandIDs.getCurrentFileContent = 'notebook-intelligence:get-current-file-content';
52
54
  CommandIDs.setCurrentFileContent = 'notebook-intelligence:set-current-file-content';
53
55
  CommandIDs.openMCPConfigEditor = 'notebook-intelligence:open-mcp-config-editor';
56
+ CommandIDs.showFormInputDialog = 'notebook-intelligence:show-form-input-dialog';
54
57
  })(CommandIDs || (CommandIDs = {}));
55
58
  const DOCUMENT_WATCH_INTERVAL = 1000;
56
59
  const MAX_TOKENS = 4096;
@@ -62,6 +65,10 @@ const sparkleIcon = new LabIcon({
62
65
  name: 'notebook-intelligence:sparkles-icon',
63
66
  svgstr: sparklesSvgstr
64
67
  });
68
+ const sparkleWarningIcon = new LabIcon({
69
+ name: 'notebook-intelligence:sparkles-warning-icon',
70
+ svgstr: sparklesWarningSvgstr
71
+ });
65
72
  const emptyNotebookContent = {
66
73
  cells: [],
67
74
  metadata: {},
@@ -498,7 +505,7 @@ const plugin = {
498
505
  panel.id = 'notebook-intelligence-tab';
499
506
  panel.title.caption = 'Notebook Intelligence';
500
507
  const sidebarIcon = new LabIcon({
501
- name: 'ui-components:palette',
508
+ name: 'notebook-intelligence:sidebar-icon',
502
509
  svgstr: sparklesSvgstr
503
510
  });
504
511
  panel.title.icon = sidebarIcon;
@@ -525,6 +532,23 @@ const plugin = {
525
532
  panel.addWidget(sidebar);
526
533
  app.shell.add(panel, 'left', { rank: 1000 });
527
534
  app.shell.activateById(panel.id);
535
+ const updateSidebarIcon = () => {
536
+ if (NBIAPI.getChatEnabled()) {
537
+ panel.title.icon = sidebarIcon;
538
+ }
539
+ else {
540
+ panel.title.icon = sparkleWarningIcon;
541
+ }
542
+ };
543
+ NBIAPI.githubLoginStatusChanged.connect((_, args) => {
544
+ updateSidebarIcon();
545
+ });
546
+ NBIAPI.configChanged.connect((_, args) => {
547
+ updateSidebarIcon();
548
+ });
549
+ setTimeout(() => {
550
+ updateSidebarIcon();
551
+ }, 2000);
528
552
  app.commands.addCommand(CommandIDs.chatuserInput, {
529
553
  execute: args => {
530
554
  NBIAPI.sendChatUserInput(args.id, args.data);
@@ -594,6 +618,36 @@ const plugin = {
594
618
  return newPyFile;
595
619
  }
596
620
  });
621
+ app.commands.addCommand(CommandIDs.showFormInputDialog, {
622
+ execute: async (args) => {
623
+ const title = args.title;
624
+ const fields = args.fields;
625
+ return new Promise((resolve, reject) => {
626
+ let dialog = null;
627
+ const dialogBody = new FormInputDialogBody({
628
+ fields: fields,
629
+ onDone: (formData) => {
630
+ dialog.dispose();
631
+ resolve(formData);
632
+ }
633
+ });
634
+ dialog = new Dialog({
635
+ title: title,
636
+ hasClose: true,
637
+ body: dialogBody,
638
+ buttons: []
639
+ });
640
+ dialog
641
+ .launch()
642
+ .then((result) => {
643
+ reject();
644
+ })
645
+ .catch(() => {
646
+ reject(new Error('Failed to show form input dialog'));
647
+ });
648
+ });
649
+ }
650
+ });
597
651
  app.commands.addCommand(CommandIDs.createNewNotebookFromPython, {
598
652
  execute: async (args) => {
599
653
  var _a;
@@ -922,28 +976,32 @@ const plugin = {
922
976
  dialog.launch();
923
977
  }
924
978
  });
979
+ const createNewSettingsWidget = () => {
980
+ const settingsPanel = new SettingsPanel({
981
+ onSave: () => {
982
+ NBIAPI.fetchCapabilities();
983
+ },
984
+ onEditMCPConfigClicked: () => {
985
+ app.commands.execute('notebook-intelligence:open-mcp-config-editor');
986
+ }
987
+ });
988
+ const widget = new MainAreaWidget({ content: settingsPanel });
989
+ widget.id = 'nbi-settings';
990
+ widget.title.label = 'NBI Settings';
991
+ widget.title.closable = true;
992
+ return widget;
993
+ };
994
+ let settingsWidget = createNewSettingsWidget();
925
995
  app.commands.addCommand(CommandIDs.openConfigurationDialog, {
926
996
  label: 'Notebook Intelligence Settings',
927
997
  execute: args => {
928
- let dialog = null;
929
- const dialogBody = new ConfigurationDialogBody({
930
- onSave: () => {
931
- dialog === null || dialog === void 0 ? void 0 : dialog.dispose();
932
- NBIAPI.fetchCapabilities();
933
- },
934
- onEditMCPConfigClicked: () => {
935
- dialog === null || dialog === void 0 ? void 0 : dialog.dispose();
936
- app.commands.execute('notebook-intelligence:open-mcp-config-editor');
937
- }
938
- });
939
- dialog = new Dialog({
940
- title: 'Notebook Intelligence Settings',
941
- hasClose: true,
942
- body: dialogBody,
943
- buttons: []
944
- });
945
- dialog.node.classList.add('config-dialog-container');
946
- dialog.launch();
998
+ if (settingsWidget.isDisposed) {
999
+ settingsWidget = createNewSettingsWidget();
1000
+ }
1001
+ if (!settingsWidget.isAttached) {
1002
+ app.shell.add(settingsWidget, 'main');
1003
+ }
1004
+ app.shell.activateById(settingsWidget.id);
947
1005
  }
948
1006
  });
949
1007
  app.commands.addCommand(CommandIDs.openMCPConfigEditor, {
@@ -1138,6 +1196,8 @@ const plugin = {
1138
1196
  existingCode,
1139
1197
  prefix: prefix,
1140
1198
  suffix: suffix,
1199
+ language: ActiveDocumentWatcher.activeDocumentInfo.language,
1200
+ filename: ActiveDocumentWatcher.activeDocumentInfo.filePath,
1141
1201
  onRequestSubmitted: (prompt) => {
1142
1202
  userPrompt = prompt;
1143
1203
  generatedContent = '';