@notebook-intelligence/notebook-intelligence 3.1.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/lib/api.d.ts +11 -0
- package/lib/api.js +27 -8
- package/lib/chat-sidebar.js +65 -22
- package/lib/components/ask-user-question.d.ts +2 -0
- package/lib/components/ask-user-question.js +58 -0
- package/lib/components/checkbox.js +8 -3
- package/lib/components/settings-panel.js +143 -2
- package/lib/index.js +12 -8
- package/lib/tokens.d.ts +5 -2
- package/lib/tokens.js +3 -0
- package/package.json +1 -1
- package/src/api.ts +40 -12
- package/src/chat-sidebar.tsx +102 -36
- package/src/components/ask-user-question.tsx +108 -0
- package/src/components/checkbox.tsx +15 -4
- package/src/components/settings-panel.tsx +321 -1
- package/src/index.ts +16 -7
- package/src/tokens.ts +5 -2
- package/style/base.css +93 -0
- package/style/claude.svg +1 -0
package/README.md
CHANGED
|
@@ -4,6 +4,27 @@ Notebook Intelligence (NBI) is an AI coding assistant and extensible AI framewor
|
|
|
4
4
|
|
|
5
5
|
## Feature Highlights
|
|
6
6
|
|
|
7
|
+
### Claude Mode
|
|
8
|
+
|
|
9
|
+
Notebook Intelligence provides a dedicated mode for [Claude Code](https://code.claude.com/) integration. In **Claude mode**, NBI uses Claude Code for AI Agent Chat UI and Claude models for inline chat (in editors) and auto-complete suggestions.
|
|
10
|
+
|
|
11
|
+
This integration brings the AI tools and features supported by Claude Code such as built-in tools, skills, MCP servers, custom commands and many more to JupyterLab.
|
|
12
|
+
|
|
13
|
+
<img src="media/claude-chat.png" alt="Claude mode" width=500 />
|
|
14
|
+
|
|
15
|
+
#### Claude Configuration
|
|
16
|
+
|
|
17
|
+
You can configure the Claude settings in the NBI Settings dialog. You can access this dialog by using the gear icon in the NBI Chat UI or from JupyterLab Settings menu -> Notebook Intelligence Settings. Toggle the `Enable Claude mode` option to enable Claude mode. The other options are as follows:
|
|
18
|
+
|
|
19
|
+
- **Chat model**: Select the Claude model to use for Agent Chat UI and inline chat.
|
|
20
|
+
- **Auto-complete model**: Select the Claude model to use for auto-complete suggestions.
|
|
21
|
+
- **Chat Agent setting sources**: Select the setting sources to use for Claude Code. You can choose to use user settings, project settings or both. These settings are the standard Claude Code settings such as tools, skills, MCP servers, custom commands and many more that are saved in the user's home directory and project directory. See [Claude Code documentation](https://code.claude.com/docs/en/settings) for more details.
|
|
22
|
+
- **Chat Agent tools**: Select the tools to activate in the Agent Chat UI. `Claude Code tools` are always activated. `Jupyter UI tools` are the tools that are provided by NBI to interact with JupyterLab UI (authoring notebooks, running cells, etc.).
|
|
23
|
+
- **API key**: Enter your Claude API key.
|
|
24
|
+
- **Base URL**: Enter your Claude base URL.
|
|
25
|
+
|
|
26
|
+
<img src="media/claude-settings.png" alt="Claude settings" width=700 />
|
|
27
|
+
|
|
7
28
|
### Agent Mode
|
|
8
29
|
|
|
9
30
|
In Agent Mode, built-in AI agent creates, edits and executes notebooks for you interactively. It can detect issues in the cells and fix for you.
|
package/lib/api.d.ts
CHANGED
|
@@ -10,6 +10,15 @@ export interface IDeviceVerificationInfo {
|
|
|
10
10
|
verificationURI: string;
|
|
11
11
|
userCode: string;
|
|
12
12
|
}
|
|
13
|
+
export declare enum ClaudeModelType {
|
|
14
|
+
Default = "",
|
|
15
|
+
ClaudeOpus45 = "claude-opus-4-5",
|
|
16
|
+
ClaudeHaiku45 = "claude-haiku-4-5"
|
|
17
|
+
}
|
|
18
|
+
export declare enum ClaudeToolType {
|
|
19
|
+
ClaudeCodeTools = "claude-code:built-in-tools",
|
|
20
|
+
JupyterUITools = "nbi:built-in-jupyter-ui-tools"
|
|
21
|
+
}
|
|
13
22
|
export declare class NBIConfig {
|
|
14
23
|
get userHomeDir(): string;
|
|
15
24
|
get userConfigDir(): string;
|
|
@@ -26,6 +35,8 @@ export declare class NBIConfig {
|
|
|
26
35
|
getMCPServer(serverId: string): any;
|
|
27
36
|
getMCPServerPrompt(serverId: string, promptName: string): any;
|
|
28
37
|
get mcpServerSettings(): any;
|
|
38
|
+
get claudeSettings(): any;
|
|
39
|
+
get isInClaudeCodeMode(): boolean;
|
|
29
40
|
capabilities: any;
|
|
30
41
|
chatParticipants: IChatParticipant[];
|
|
31
42
|
changed: Signal<this, void>;
|
package/lib/api.js
CHANGED
|
@@ -13,6 +13,17 @@ export var GitHubCopilotLoginStatus;
|
|
|
13
13
|
GitHubCopilotLoginStatus["LoggingIn"] = "LOGGING_IN";
|
|
14
14
|
GitHubCopilotLoginStatus["LoggedIn"] = "LOGGED_IN";
|
|
15
15
|
})(GitHubCopilotLoginStatus || (GitHubCopilotLoginStatus = {}));
|
|
16
|
+
export var ClaudeModelType;
|
|
17
|
+
(function (ClaudeModelType) {
|
|
18
|
+
ClaudeModelType["Default"] = "";
|
|
19
|
+
ClaudeModelType["ClaudeOpus45"] = "claude-opus-4-5";
|
|
20
|
+
ClaudeModelType["ClaudeHaiku45"] = "claude-haiku-4-5";
|
|
21
|
+
})(ClaudeModelType || (ClaudeModelType = {}));
|
|
22
|
+
export var ClaudeToolType;
|
|
23
|
+
(function (ClaudeToolType) {
|
|
24
|
+
ClaudeToolType["ClaudeCodeTools"] = "claude-code:built-in-tools";
|
|
25
|
+
ClaudeToolType["JupyterUITools"] = "nbi:built-in-jupyter-ui-tools";
|
|
26
|
+
})(ClaudeToolType || (ClaudeToolType = {}));
|
|
16
27
|
export class NBIConfig {
|
|
17
28
|
constructor() {
|
|
18
29
|
this.capabilities = {};
|
|
@@ -69,6 +80,12 @@ export class NBIConfig {
|
|
|
69
80
|
get mcpServerSettings() {
|
|
70
81
|
return this.capabilities.mcp_server_settings;
|
|
71
82
|
}
|
|
83
|
+
get claudeSettings() {
|
|
84
|
+
return this.capabilities.claude_settings;
|
|
85
|
+
}
|
|
86
|
+
get isInClaudeCodeMode() {
|
|
87
|
+
return this.claudeSettings.enabled === true;
|
|
88
|
+
}
|
|
72
89
|
}
|
|
73
90
|
class NBIAPI {
|
|
74
91
|
static async initialize() {
|
|
@@ -77,7 +94,8 @@ class NBIAPI {
|
|
|
77
94
|
NBIAPI.initializeWebsocket();
|
|
78
95
|
this._messageReceived.connect((_, msg) => {
|
|
79
96
|
msg = JSON.parse(msg);
|
|
80
|
-
if (msg.type === BackendMessageType.MCPServerStatusChange
|
|
97
|
+
if (msg.type === BackendMessageType.MCPServerStatusChange ||
|
|
98
|
+
msg.type === BackendMessageType.ClaudeCodeStatusChange) {
|
|
81
99
|
this.fetchCapabilities();
|
|
82
100
|
}
|
|
83
101
|
else if (msg.type === BackendMessageType.GitHubCopilotLoginStatusChange) {
|
|
@@ -116,15 +134,16 @@ class NBIAPI {
|
|
|
116
134
|
NBIAPI.getLoginStatus() === GitHubCopilotLoginStatus.NotLoggedIn);
|
|
117
135
|
}
|
|
118
136
|
static getChatEnabled() {
|
|
119
|
-
return this.config.
|
|
120
|
-
|
|
121
|
-
|
|
137
|
+
return (this.config.isInClaudeCodeMode ||
|
|
138
|
+
(this.config.chatModel.provider === GITHUB_COPILOT_PROVIDER_ID
|
|
139
|
+
? !this.getGHLoginRequired()
|
|
140
|
+
: this.config.llmProviders.find(provider => provider.id === this.config.chatModel.provider)));
|
|
122
141
|
}
|
|
123
142
|
static getInlineCompletionEnabled() {
|
|
124
|
-
return this.config.
|
|
125
|
-
GITHUB_COPILOT_PROVIDER_ID
|
|
126
|
-
|
|
127
|
-
|
|
143
|
+
return (this.config.isInClaudeCodeMode ||
|
|
144
|
+
(this.config.inlineCompletionModel.provider === GITHUB_COPILOT_PROVIDER_ID
|
|
145
|
+
? !this.getGHLoginRequired()
|
|
146
|
+
: this.config.llmProviders.find(provider => provider.id === this.config.inlineCompletionModel.provider)));
|
|
128
147
|
}
|
|
129
148
|
static async loginToGitHub() {
|
|
130
149
|
this._loginStatus = GitHubCopilotLoginStatus.ActivatingDevice;
|
package/lib/chat-sidebar.js
CHANGED
|
@@ -4,7 +4,7 @@ import { ReactWidget } from '@jupyterlab/apputils';
|
|
|
4
4
|
import { UUID } from '@lumino/coreutils';
|
|
5
5
|
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
|
|
6
6
|
import { NBIAPI, GitHubCopilotLoginStatus } from './api';
|
|
7
|
-
import { BackendMessageType, BuiltinToolsetType, ContextType, RequestDataType, ResponseStreamDataType, TelemetryEventType } from './tokens';
|
|
7
|
+
import { BackendMessageType, BuiltinToolsetType, CLAUDE_CODE_CHAT_PARTICIPANT_ID, ContextType, RequestDataType, ResponseStreamDataType, TelemetryEventType } from './tokens';
|
|
8
8
|
import { MarkdownRenderer as OriginalMarkdownRenderer } from './markdown-renderer';
|
|
9
9
|
const MarkdownRenderer = memo(OriginalMarkdownRenderer);
|
|
10
10
|
import copySvgstr from '../style/icons/copy.svg';
|
|
@@ -14,6 +14,8 @@ import { VscSend, VscStopCircle, VscEye, VscEyeClosed, VscTriangleRight, VscTria
|
|
|
14
14
|
import { extractLLMGeneratedCode, isDarkTheme } from './utils';
|
|
15
15
|
import { CheckBoxItem } from './components/checkbox';
|
|
16
16
|
import { mcpServerSettingsToEnabledState } from './components/mcp-util';
|
|
17
|
+
import claudeSvg from '../style/claude.svg';
|
|
18
|
+
import { AskUserQuestion } from './components/ask-user-question';
|
|
17
19
|
export var RunChatCompletionType;
|
|
18
20
|
(function (RunChatCompletionType) {
|
|
19
21
|
RunChatCompletionType[RunChatCompletionType["Chat"] = 0] = "Chat";
|
|
@@ -261,10 +263,11 @@ function ChatResponse(props) {
|
|
|
261
263
|
? 'Output'
|
|
262
264
|
: `Output (${Math.floor(item.reasoningTime)} s)`;
|
|
263
265
|
};
|
|
266
|
+
const chatParticipantId = ((_a = msg.participant) === null || _a === void 0 ? void 0 : _a.id) || 'default';
|
|
264
267
|
return (React.createElement("div", { className: `chat-message chat-message-${msg.from}`, "data-render-count": renderCount },
|
|
265
268
|
React.createElement("div", { className: "chat-message-header" },
|
|
266
269
|
React.createElement("div", { className: "chat-message-from" },
|
|
267
|
-
((
|
|
270
|
+
((_b = msg.participant) === null || _b === void 0 ? void 0 : _b.iconPath) && (React.createElement("div", { className: `chat-message-from-icon chat-message-from-icon-${chatParticipantId} ${isDarkTheme() ? 'dark' : ''}` },
|
|
268
271
|
React.createElement("img", { src: msg.participant.iconPath }))),
|
|
269
272
|
React.createElement("div", { className: "chat-message-from-title" }, msg.from === 'user'
|
|
270
273
|
? 'User'
|
|
@@ -329,6 +332,32 @@ function ChatResponse(props) {
|
|
|
329
332
|
runCommand('notebook-intelligence:chat-user-input', item.content.cancelArgs);
|
|
330
333
|
} },
|
|
331
334
|
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, item.content.cancelLabel))));
|
|
335
|
+
case ResponseStreamDataType.AskUserQuestion:
|
|
336
|
+
return answeredForms.get(item.id) ===
|
|
337
|
+
'confirmed' ? null : answeredForms.get(item.id) ===
|
|
338
|
+
'canceled' ? (React.createElement("div", null, "\u2716 Canceled")) : (React.createElement("div", { className: "chat-confirmation-form ask-user-question", key: `key-${index}` },
|
|
339
|
+
React.createElement(AskUserQuestion, { userQuestions: item, onSubmit: (selectedAnswers) => {
|
|
340
|
+
markFormConfirmed(item.id);
|
|
341
|
+
runCommand('notebook-intelligence:chat-user-input', {
|
|
342
|
+
id: item.content.identifier.id,
|
|
343
|
+
data: {
|
|
344
|
+
callback_id: item.content.identifier.callback_id,
|
|
345
|
+
data: {
|
|
346
|
+
confirmed: true,
|
|
347
|
+
selectedAnswers
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
}, onCancel: () => {
|
|
352
|
+
markFormCanceled(item.id);
|
|
353
|
+
runCommand('notebook-intelligence:chat-user-input', {
|
|
354
|
+
id: item.content.identifier.id,
|
|
355
|
+
data: {
|
|
356
|
+
callback_id: item.content.identifier.callback_id,
|
|
357
|
+
data: { confirmed: false }
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
} })));
|
|
332
361
|
}
|
|
333
362
|
return null;
|
|
334
363
|
}),
|
|
@@ -338,15 +367,15 @@ const MemoizedChatResponse = memo(ChatResponse);
|
|
|
338
367
|
async function submitCompletionRequest(request, responseEmitter) {
|
|
339
368
|
switch (request.type) {
|
|
340
369
|
case RunChatCompletionType.Chat:
|
|
341
|
-
return NBIAPI.chatRequest(request.messageId, request.chatId, request.content, request.language || 'python', request.currentDirectory || '', request.filename || '
|
|
370
|
+
return NBIAPI.chatRequest(request.messageId, request.chatId, request.content, request.language || 'python', request.currentDirectory || '', request.filename || '', request.additionalContext || [], request.chatMode, request.toolSelections || {}, responseEmitter);
|
|
342
371
|
case RunChatCompletionType.ExplainThis:
|
|
343
372
|
case RunChatCompletionType.FixThis:
|
|
344
373
|
case RunChatCompletionType.ExplainThisOutput:
|
|
345
374
|
case RunChatCompletionType.TroubleshootThisOutput: {
|
|
346
|
-
return NBIAPI.chatRequest(request.messageId, request.chatId, request.content, request.language || 'python', request.currentDirectory || '', request.filename || '
|
|
375
|
+
return NBIAPI.chatRequest(request.messageId, request.chatId, request.content, request.language || 'python', request.currentDirectory || '', request.filename || '', [], 'ask', {}, responseEmitter);
|
|
347
376
|
}
|
|
348
377
|
case RunChatCompletionType.GenerateCode:
|
|
349
|
-
return NBIAPI.generateCode(request.chatId, request.content, request.prefix || '', request.suffix || '', request.existingCode || '', request.language || 'python', request.filename || '
|
|
378
|
+
return NBIAPI.generateCode(request.chatId, request.content, request.prefix || '', request.suffix || '', request.existingCode || '', request.language || 'python', request.filename || '', responseEmitter);
|
|
350
379
|
}
|
|
351
380
|
}
|
|
352
381
|
function SidebarComponent(props) {
|
|
@@ -689,23 +718,36 @@ function SidebarComponent(props) {
|
|
|
689
718
|
useEffect(() => {
|
|
690
719
|
var _a;
|
|
691
720
|
const prefixes = [];
|
|
692
|
-
if (
|
|
693
|
-
const
|
|
694
|
-
|
|
695
|
-
const
|
|
696
|
-
const commands = participant.commands;
|
|
697
|
-
const participantPrefix = id === 'default' ? '' : `@${id}`;
|
|
698
|
-
if (participantPrefix !== '') {
|
|
699
|
-
prefixes.push(participantPrefix);
|
|
700
|
-
}
|
|
701
|
-
const commandPrefix = participantPrefix === '' ? '' : `${participantPrefix} `;
|
|
721
|
+
if (NBIAPI.config.isInClaudeCodeMode) {
|
|
722
|
+
const claudeChatParticipant = NBIAPI.config.chatParticipants.find(participant => participant.id === CLAUDE_CODE_CHAT_PARTICIPANT_ID);
|
|
723
|
+
if (claudeChatParticipant) {
|
|
724
|
+
const commands = claudeChatParticipant.commands;
|
|
702
725
|
for (const command of commands) {
|
|
703
|
-
prefixes.push(
|
|
726
|
+
prefixes.push(`/${command}`);
|
|
704
727
|
}
|
|
705
728
|
}
|
|
729
|
+
prefixes.push('/enter-plan-mode');
|
|
730
|
+
prefixes.push('/exit-plan-mode');
|
|
706
731
|
}
|
|
707
732
|
else {
|
|
708
|
-
|
|
733
|
+
if (chatMode === 'ask') {
|
|
734
|
+
const chatParticipants = NBIAPI.config.chatParticipants;
|
|
735
|
+
for (const participant of chatParticipants) {
|
|
736
|
+
const id = participant.id;
|
|
737
|
+
const commands = participant.commands;
|
|
738
|
+
const participantPrefix = id === 'default' ? '' : `@${id}`;
|
|
739
|
+
if (participantPrefix !== '') {
|
|
740
|
+
prefixes.push(participantPrefix);
|
|
741
|
+
}
|
|
742
|
+
const commandPrefix = participantPrefix === '' ? '' : `${participantPrefix} `;
|
|
743
|
+
for (const command of commands) {
|
|
744
|
+
prefixes.push(`${commandPrefix}/${command}`);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
else {
|
|
749
|
+
prefixes.push('/clear');
|
|
750
|
+
}
|
|
709
751
|
}
|
|
710
752
|
const mcpServers = NBIAPI.config.toolConfig.mcpServers;
|
|
711
753
|
const mcpServerSettings = NBIAPI.config.mcpServerSettings;
|
|
@@ -1289,7 +1331,7 @@ function SidebarComponent(props) {
|
|
|
1289
1331
|
}, title: "Select chat participant" }, "@"))),
|
|
1290
1332
|
React.createElement("div", { style: { flexGrow: 1 } }),
|
|
1291
1333
|
React.createElement("div", { className: "chat-mode-widgets-container" },
|
|
1292
|
-
React.createElement("div", null,
|
|
1334
|
+
!NBIAPI.config.isInClaudeCodeMode && (React.createElement("div", null,
|
|
1293
1335
|
React.createElement("select", { className: "chat-mode-select", title: "Chat mode", value: chatMode, onChange: event => {
|
|
1294
1336
|
if (event.target.value === 'ask') {
|
|
1295
1337
|
setToolSelections(toolSelectionsEmpty);
|
|
@@ -1298,12 +1340,13 @@ function SidebarComponent(props) {
|
|
|
1298
1340
|
setChatMode(event.target.value);
|
|
1299
1341
|
} },
|
|
1300
1342
|
React.createElement("option", { value: "ask" }, "Ask"),
|
|
1301
|
-
React.createElement("option", { value: "agent" }, "Agent"))),
|
|
1302
|
-
chatMode !== 'ask' && (React.createElement("div", { className: `user-input-footer-button tools-button ${unsafeToolSelected ? 'tools-button-warning' : selectedToolCount > 0 ? 'tools-button-active' : ''}`, onClick: () => handleChatToolsButtonClick(), title: unsafeToolSelected
|
|
1343
|
+
React.createElement("option", { value: "agent" }, "Agent")))),
|
|
1344
|
+
chatMode !== 'ask' && !NBIAPI.config.isInClaudeCodeMode && (React.createElement("div", { className: `user-input-footer-button tools-button ${unsafeToolSelected ? 'tools-button-warning' : selectedToolCount > 0 ? 'tools-button-active' : ''}`, onClick: () => handleChatToolsButtonClick(), title: unsafeToolSelected
|
|
1303
1345
|
? `Tool selection can cause irreversible changes! Review each tool execution carefully.\n${toolSelectionTitle}`
|
|
1304
1346
|
: toolSelectionTitle },
|
|
1305
1347
|
React.createElement(VscTools, null),
|
|
1306
|
-
selectedToolCount > 0 && React.createElement(React.Fragment, null, selectedToolCount)))
|
|
1348
|
+
selectedToolCount > 0 && React.createElement(React.Fragment, null, selectedToolCount))),
|
|
1349
|
+
NBIAPI.config.isInClaudeCodeMode && (React.createElement("span", { title: "Claude mode", className: "claude-icon", dangerouslySetInnerHTML: { __html: claudeSvg } }))),
|
|
1307
1350
|
React.createElement("div", null,
|
|
1308
1351
|
React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled send-button", onClick: () => handleSubmitStopChatButtonClick(), disabled: prompt.length === 0 && !copilotRequestInProgress }, copilotRequestInProgress ? React.createElement(VscStopCircle, null) : React.createElement(VscSend, null)))),
|
|
1309
1352
|
showPopover && prefixSuggestions.length > 0 && (React.createElement("div", { className: "user-input-autocomplete" }, prefixSuggestions.map((prefix, index) => (React.createElement("div", { key: `key-${index}`, className: `user-input-autocomplete-item ${index === selectedPrefixSuggestionIndex ? 'selected' : ''}`, "data-value": prefix, onClick: event => prefixSuggestionSelected(event) }, prefix))))),
|
|
@@ -1445,7 +1488,7 @@ function InlinePromptComponent(props) {
|
|
|
1445
1488
|
type: RunChatCompletionType.GenerateCode,
|
|
1446
1489
|
content: prompt,
|
|
1447
1490
|
language: props.language || 'python',
|
|
1448
|
-
filename: props.filename || '
|
|
1491
|
+
filename: props.filename || '',
|
|
1449
1492
|
prefix: props.prefix,
|
|
1450
1493
|
suffix: props.suffix,
|
|
1451
1494
|
existingCode: props.existingCode,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
export function AskUserQuestion(props) {
|
|
4
|
+
const userQuestions = props.userQuestions.content;
|
|
5
|
+
const [selectedAnswers, setSelectedAnswers] = useState({});
|
|
6
|
+
const onOptionSelected = (question, option) => {
|
|
7
|
+
var _a, _b, _c;
|
|
8
|
+
if (question.multiSelect) {
|
|
9
|
+
if ((_a = selectedAnswers[question.question]) === null || _a === void 0 ? void 0 : _a.includes(option.label)) {
|
|
10
|
+
setSelectedAnswers({
|
|
11
|
+
...selectedAnswers,
|
|
12
|
+
[question.question]: ((_b = selectedAnswers[question.question]) !== null && _b !== void 0 ? _b : []).filter((o) => o !== option.label)
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
setSelectedAnswers({
|
|
17
|
+
...selectedAnswers,
|
|
18
|
+
[question.question]: [
|
|
19
|
+
...((_c = selectedAnswers[question.question]) !== null && _c !== void 0 ? _c : []),
|
|
20
|
+
option.label
|
|
21
|
+
]
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
setSelectedAnswers({
|
|
27
|
+
...selectedAnswers,
|
|
28
|
+
[question.question]: [option.label]
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
return (React.createElement(React.Fragment, null,
|
|
33
|
+
userQuestions.title ? (React.createElement("div", null,
|
|
34
|
+
React.createElement("b", null, userQuestions.title))) : null,
|
|
35
|
+
userQuestions.message ? React.createElement("div", null, userQuestions.message) : null,
|
|
36
|
+
userQuestions.questions.map((question) => (React.createElement("div", { className: "ask-user-question-container", key: question.question },
|
|
37
|
+
React.createElement("form", { className: "ask-user-question-form" },
|
|
38
|
+
React.createElement("div", { className: "ask-user-question-question" }, question.question),
|
|
39
|
+
React.createElement("div", { className: "ask-user-question-header" }, question.header),
|
|
40
|
+
React.createElement("div", { className: "ask-user-question-options" }, question.options.map((option) => {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
return (React.createElement("div", { className: "ask-user-question-option", key: option.label },
|
|
43
|
+
React.createElement("div", { className: "ask-user-question-option-input-container" },
|
|
44
|
+
React.createElement("input", { id: option.label, type: "checkbox", checked: (_b = (_a = selectedAnswers[question.question]) === null || _a === void 0 ? void 0 : _a.includes(option.label)) !== null && _b !== void 0 ? _b : false, onChange: () => onOptionSelected(question, option) }),
|
|
45
|
+
React.createElement("label", { htmlFor: option.label, className: "ask-user-question-option-label-container" },
|
|
46
|
+
React.createElement("div", { className: "ask-user-question-option-label" }, option.label),
|
|
47
|
+
React.createElement("div", { className: "ask-user-question-option-description" }, option.description)))));
|
|
48
|
+
})))))),
|
|
49
|
+
React.createElement("div", { className: "ask-user-question-footer" },
|
|
50
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: () => {
|
|
51
|
+
props.onSubmit(selectedAnswers);
|
|
52
|
+
} },
|
|
53
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, userQuestions.submitLabel)),
|
|
54
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-reject jp-mod-styled", onClick: () => {
|
|
55
|
+
props.onCancel();
|
|
56
|
+
} },
|
|
57
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, userQuestions.cancelLabel)))));
|
|
58
|
+
}
|
|
@@ -3,9 +3,14 @@ import React from 'react';
|
|
|
3
3
|
import { MdOutlineCheckBoxOutlineBlank, MdCheckBox } from 'react-icons/md';
|
|
4
4
|
export function CheckBoxItem(props) {
|
|
5
5
|
const indent = props.indent || 0;
|
|
6
|
-
|
|
6
|
+
const disabled = props.disabled || false;
|
|
7
|
+
return (React.createElement("div", { className: `checkbox-item checkbox-item-indent-${indent} ${props.header ? 'checkbox-item-header' : ''}`, title: props.tooltip || props.title || '', onClick: event => {
|
|
8
|
+
if (!disabled) {
|
|
9
|
+
props.onClick(event);
|
|
10
|
+
}
|
|
11
|
+
} },
|
|
7
12
|
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),
|
|
13
|
+
props.checked ? (React.createElement(MdCheckBox, { className: "checkbox-icon", style: { opacity: disabled ? 0.5 : 1 } })) : (React.createElement(MdOutlineCheckBoxOutlineBlank, { className: "checkbox-icon", style: { opacity: disabled ? 0.5 : 1 } })),
|
|
14
|
+
React.createElement("span", { style: { opacity: disabled ? 0.5 : 1 } }, props.label)),
|
|
10
15
|
props.title && (React.createElement("div", { className: "checkbox-item-description" }, props.title))));
|
|
11
16
|
}
|
|
@@ -4,10 +4,11 @@ import { ReactWidget } from '@jupyterlab/apputils';
|
|
|
4
4
|
import { VscWarning } from 'react-icons/vsc';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import copySvgstr from '../../style/icons/copy.svg';
|
|
7
|
-
import { NBIAPI } from '../api';
|
|
7
|
+
import { ClaudeModelType, ClaudeToolType, NBIAPI } from '../api';
|
|
8
8
|
import { CheckBoxItem } from './checkbox';
|
|
9
9
|
import { PillItem } from './pill';
|
|
10
10
|
import { mcpServerSettingsToEnabledState } from './mcp-util';
|
|
11
|
+
import claudeSvg from '../../style/claude.svg';
|
|
11
12
|
const OPENAI_COMPATIBLE_CHAT_MODEL_ID = 'openai-compatible-chat-model';
|
|
12
13
|
const LITELLM_COMPATIBLE_CHAT_MODEL_ID = 'litellm-compatible-chat-model';
|
|
13
14
|
const OPENAI_COMPATIBLE_INLINE_COMPLETION_MODEL_ID = 'openai-compatible-inline-completion-model';
|
|
@@ -32,7 +33,8 @@ function SettingsPanelComponent(props) {
|
|
|
32
33
|
React.createElement(SettingsPanelTabsComponent, { onTabSelected: onTabSelected, activeTab: activeTab })),
|
|
33
34
|
React.createElement("div", { className: "nbi-settings-panel-tab-content" },
|
|
34
35
|
activeTab === 'general' && (React.createElement(SettingsPanelComponentGeneral, { onSave: props.onSave, onEditMCPConfigClicked: props.onEditMCPConfigClicked })),
|
|
35
|
-
activeTab === 'mcp-servers' && (React.createElement(SettingsPanelComponentMCPServers, { onEditMCPConfigClicked: props.onEditMCPConfigClicked }))
|
|
36
|
+
activeTab === 'mcp-servers' && (React.createElement(SettingsPanelComponentMCPServers, { onEditMCPConfigClicked: props.onEditMCPConfigClicked })),
|
|
37
|
+
activeTab === 'claude' && (React.createElement(SettingsPanelComponentClaude, { onEditMCPConfigClicked: props.onEditMCPConfigClicked })))));
|
|
36
38
|
}
|
|
37
39
|
function SettingsPanelTabsComponent(props) {
|
|
38
40
|
const [activeTab, setActiveTab] = useState(props.activeTab);
|
|
@@ -41,6 +43,12 @@ function SettingsPanelTabsComponent(props) {
|
|
|
41
43
|
setActiveTab('general');
|
|
42
44
|
props.onTabSelected('general');
|
|
43
45
|
} }, "General"),
|
|
46
|
+
React.createElement("div", { className: `nbi-settings-panel-tab ${activeTab === 'claude' ? 'active' : ''}`, onClick: () => {
|
|
47
|
+
setActiveTab('claude');
|
|
48
|
+
props.onTabSelected('claude');
|
|
49
|
+
} },
|
|
50
|
+
React.createElement("span", { className: "claude-icon", dangerouslySetInnerHTML: { __html: claudeSvg } }),
|
|
51
|
+
"Claude"),
|
|
44
52
|
React.createElement("div", { className: `nbi-settings-panel-tab ${activeTab === 'mcp-servers' ? 'active' : ''}`, onClick: () => {
|
|
45
53
|
setActiveTab('mcp-servers');
|
|
46
54
|
props.onTabSelected('mcp-servers');
|
|
@@ -378,3 +386,136 @@ function SettingsPanelComponentMCPServers(props) {
|
|
|
378
386
|
React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", style: { width: 'max-content' }, onClick: props.onEditMCPConfigClicked },
|
|
379
387
|
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Add / Edit")))))))));
|
|
380
388
|
}
|
|
389
|
+
function SettingsPanelComponentClaude(props) {
|
|
390
|
+
var _a, _b, _c, _d, _e, _f;
|
|
391
|
+
const nbiConfig = NBIAPI.config;
|
|
392
|
+
const claudeSettingsRef = useRef(nbiConfig.claudeSettings);
|
|
393
|
+
const [_renderCount, setRenderCount] = useState(1);
|
|
394
|
+
const [claudeEnabled, setClaudeEnabled] = useState(nbiConfig.isInClaudeCodeMode);
|
|
395
|
+
const [chatModel, setChatModel] = useState((_a = nbiConfig.claudeSettings.chat_model) !== null && _a !== void 0 ? _a : ClaudeModelType.Default);
|
|
396
|
+
const [inlineCompletionModel, setInlineCompletionModel] = useState((_b = nbiConfig.claudeSettings.inline_completion_model) !== null && _b !== void 0 ? _b : ClaudeModelType.Default);
|
|
397
|
+
const [apiKey, setApiKey] = useState((_c = nbiConfig.claudeSettings.api_key) !== null && _c !== void 0 ? _c : '');
|
|
398
|
+
const [baseUrl, setBaseUrl] = useState((_d = nbiConfig.claudeSettings.base_url) !== null && _d !== void 0 ? _d : '');
|
|
399
|
+
const [settingSources, setSettingSources] = useState((_e = nbiConfig.claudeSettings.setting_sources) !== null && _e !== void 0 ? _e : []);
|
|
400
|
+
const [tools, setTools] = useState((_f = nbiConfig.claudeSettings.tools) !== null && _f !== void 0 ? _f : [
|
|
401
|
+
ClaudeToolType.ClaudeCodeTools,
|
|
402
|
+
ClaudeToolType.JupyterUITools
|
|
403
|
+
]);
|
|
404
|
+
useEffect(() => {
|
|
405
|
+
NBIAPI.configChanged.connect(() => {
|
|
406
|
+
claudeSettingsRef.current = nbiConfig.claudeSettings;
|
|
407
|
+
setRenderCount(renderCount => renderCount + 1);
|
|
408
|
+
});
|
|
409
|
+
}, []);
|
|
410
|
+
const syncSettingsToServerState = () => {
|
|
411
|
+
NBIAPI.setConfig({
|
|
412
|
+
claude_settings: {
|
|
413
|
+
enabled: claudeEnabled,
|
|
414
|
+
chat_model: chatModel,
|
|
415
|
+
inline_completion_model: inlineCompletionModel,
|
|
416
|
+
api_key: apiKey,
|
|
417
|
+
base_url: baseUrl,
|
|
418
|
+
setting_sources: settingSources,
|
|
419
|
+
tools: tools
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
};
|
|
423
|
+
useEffect(() => {
|
|
424
|
+
syncSettingsToServerState();
|
|
425
|
+
}, [
|
|
426
|
+
claudeEnabled,
|
|
427
|
+
chatModel,
|
|
428
|
+
inlineCompletionModel,
|
|
429
|
+
apiKey,
|
|
430
|
+
baseUrl,
|
|
431
|
+
settingSources,
|
|
432
|
+
tools
|
|
433
|
+
]);
|
|
434
|
+
return (React.createElement("div", { className: "config-dialog" },
|
|
435
|
+
React.createElement("div", { className: "config-dialog-body" },
|
|
436
|
+
React.createElement("div", { className: "model-config-section" },
|
|
437
|
+
React.createElement("div", { className: "model-config-section-header" }, "Enable Claude mode"),
|
|
438
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
439
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
440
|
+
React.createElement("span", null,
|
|
441
|
+
"This requires a",
|
|
442
|
+
' ',
|
|
443
|
+
React.createElement("a", { href: "https://claude.ai", target: "_blank" }, "Claude"),
|
|
444
|
+
' ',
|
|
445
|
+
"account and",
|
|
446
|
+
' ',
|
|
447
|
+
React.createElement("a", { href: "https://code.claude.com/", target: "_blank" }, "Claude Code"),
|
|
448
|
+
' ',
|
|
449
|
+
"installed in your system.")),
|
|
450
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
451
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
452
|
+
React.createElement("div", null,
|
|
453
|
+
React.createElement(CheckBoxItem, { header: true, label: "Enable Claude mode", checked: claudeEnabled, onClick: () => {
|
|
454
|
+
setClaudeEnabled(!claudeEnabled);
|
|
455
|
+
} })))))),
|
|
456
|
+
React.createElement("div", { className: "model-config-section" },
|
|
457
|
+
React.createElement("div", { className: "model-config-section-header" }, "Models"),
|
|
458
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
459
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
460
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
461
|
+
React.createElement("div", null, "Chat model"),
|
|
462
|
+
React.createElement("div", null,
|
|
463
|
+
React.createElement("select", { className: "jp-mod-styled", onChange: event => setChatModel(event.target.value) },
|
|
464
|
+
React.createElement("option", { value: ClaudeModelType.Default, selected: chatModel === ClaudeModelType.Default }, "Default (recommended)"),
|
|
465
|
+
React.createElement("option", { value: ClaudeModelType.ClaudeOpus45, selected: chatModel === ClaudeModelType.ClaudeOpus45 }, "Claude Opus 4.5"),
|
|
466
|
+
React.createElement("option", { value: ClaudeModelType.ClaudeHaiku45, selected: chatModel === ClaudeModelType.ClaudeHaiku45 }, "Claude Haiku 4.5")))),
|
|
467
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
468
|
+
React.createElement("div", null, "Auto-complete model"),
|
|
469
|
+
React.createElement("div", null,
|
|
470
|
+
React.createElement("select", { className: "jp-mod-styled", onChange: event => setInlineCompletionModel(event.target.value) },
|
|
471
|
+
React.createElement("option", { value: ClaudeModelType.Default, selected: inlineCompletionModel === ClaudeModelType.Default }, "Default (recommended)"),
|
|
472
|
+
React.createElement("option", { value: ClaudeModelType.ClaudeOpus45, selected: inlineCompletionModel === ClaudeModelType.ClaudeOpus45 }, "Claude Opus 4.5"),
|
|
473
|
+
React.createElement("option", { value: ClaudeModelType.ClaudeHaiku45, selected: inlineCompletionModel === ClaudeModelType.ClaudeHaiku45 }, "Claude Haiku 4.5"))))))),
|
|
474
|
+
React.createElement("div", { className: "model-config-section" },
|
|
475
|
+
React.createElement("div", { className: "model-config-section-header" }, "Chat Agent setting sources"),
|
|
476
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
477
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
478
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
479
|
+
React.createElement("div", null,
|
|
480
|
+
React.createElement(CheckBoxItem, { header: true, label: "User", checked: settingSources.includes('user'), onClick: () => {
|
|
481
|
+
setSettingSources(settingSources.includes('user')
|
|
482
|
+
? settingSources.filter((source) => source !== 'user')
|
|
483
|
+
: [...settingSources, 'user']);
|
|
484
|
+
} }))),
|
|
485
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
486
|
+
React.createElement("div", null,
|
|
487
|
+
React.createElement(CheckBoxItem, { header: true, label: "Project (Jupyter root directory)", checked: settingSources.includes('project'), onClick: () => {
|
|
488
|
+
setSettingSources(settingSources.includes('project')
|
|
489
|
+
? settingSources.filter((source) => source !== 'project')
|
|
490
|
+
: [...settingSources, 'project']);
|
|
491
|
+
} })))))),
|
|
492
|
+
React.createElement("div", { className: "model-config-section" },
|
|
493
|
+
React.createElement("div", { className: "model-config-section-header" }, "Chat Agent tools"),
|
|
494
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
495
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
496
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
497
|
+
React.createElement("div", null,
|
|
498
|
+
React.createElement(CheckBoxItem, { header: true, label: "Claude Code tools", checked: tools.includes(ClaudeToolType.ClaudeCodeTools), disabled: true, onClick: () => {
|
|
499
|
+
setTools(tools.includes(ClaudeToolType.ClaudeCodeTools)
|
|
500
|
+
? tools.filter((tool) => tool !== ClaudeToolType.ClaudeCodeTools)
|
|
501
|
+
: [...tools, ClaudeToolType.ClaudeCodeTools]);
|
|
502
|
+
} }))),
|
|
503
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
504
|
+
React.createElement("div", null,
|
|
505
|
+
React.createElement(CheckBoxItem, { header: true, label: "Jupyter UI tools", checked: tools.includes(ClaudeToolType.JupyterUITools), onClick: () => {
|
|
506
|
+
setTools(tools.includes(ClaudeToolType.JupyterUITools)
|
|
507
|
+
? tools.filter((tool) => tool !== ClaudeToolType.JupyterUITools)
|
|
508
|
+
: [...tools, ClaudeToolType.JupyterUITools]);
|
|
509
|
+
} })))))),
|
|
510
|
+
React.createElement("div", { className: "model-config-section" },
|
|
511
|
+
React.createElement("div", { className: "model-config-section-header" }, "Claude account"),
|
|
512
|
+
React.createElement("div", { className: "model-config-section-body" },
|
|
513
|
+
React.createElement("div", { className: "model-config-section-row" },
|
|
514
|
+
React.createElement("div", { className: "model-config-section-column" },
|
|
515
|
+
React.createElement("div", { className: "form-field-row" },
|
|
516
|
+
React.createElement("div", { className: "form-field-description" }, "API Key (optional)"),
|
|
517
|
+
React.createElement("input", { name: "chat-model-id-input", placeholder: "API Key", className: "jp-mod-styled", spellCheck: false, value: apiKey, onChange: event => setApiKey(event.target.value) })),
|
|
518
|
+
React.createElement("div", { className: "form-field-row" },
|
|
519
|
+
React.createElement("div", { className: "form-field-description" }, "Base URL (optional)"),
|
|
520
|
+
React.createElement("input", { name: "chat-model-id-input", placeholder: "https://api.anthropic.com", className: "jp-mod-styled", spellCheck: false, value: baseUrl, onChange: event => setBaseUrl(event.target.value) })))))))));
|
|
521
|
+
}
|
package/lib/index.js
CHANGED
|
@@ -276,9 +276,10 @@ class NBIInlineCompletionProvider {
|
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
const nbiConfig = NBIAPI.config;
|
|
279
|
-
const inlineCompletionsEnabled = nbiConfig.
|
|
280
|
-
|
|
281
|
-
|
|
279
|
+
const inlineCompletionsEnabled = nbiConfig.isInClaudeCodeMode ||
|
|
280
|
+
(nbiConfig.inlineCompletionModel.provider === GITHUB_COPILOT_PROVIDER_ID
|
|
281
|
+
? NBIAPI.getLoginStatus() === GitHubCopilotLoginStatus.LoggedIn
|
|
282
|
+
: nbiConfig.inlineCompletionModel.provider !== 'none');
|
|
282
283
|
this._telemetryEmitter.emitTelemetryEvent({
|
|
283
284
|
type: TelemetryEventType.InlineCompletionRequest,
|
|
284
285
|
data: {
|
|
@@ -767,9 +768,10 @@ const plugin = {
|
|
|
767
768
|
NBIAPI.getLoginStatus() === GitHubCopilotLoginStatus.NotLoggedIn);
|
|
768
769
|
};
|
|
769
770
|
const isChatEnabled = () => {
|
|
770
|
-
return NBIAPI.config.
|
|
771
|
-
|
|
772
|
-
|
|
771
|
+
return (NBIAPI.config.isInClaudeCodeMode ||
|
|
772
|
+
(NBIAPI.config.chatModel.provider === GITHUB_COPILOT_PROVIDER_ID
|
|
773
|
+
? !githubLoginRequired()
|
|
774
|
+
: NBIAPI.config.chatModel.provider !== 'none'));
|
|
773
775
|
};
|
|
774
776
|
const isActiveCellCodeCell = () => {
|
|
775
777
|
if (!(app.shell.currentWidget instanceof NotebookPanel)) {
|
|
@@ -1463,10 +1465,12 @@ const plugin = {
|
|
|
1463
1465
|
item: githubCopilotStatusBarItem,
|
|
1464
1466
|
align: 'right',
|
|
1465
1467
|
rank: 100,
|
|
1466
|
-
isActive: () => NBIAPI.config.
|
|
1468
|
+
isActive: () => !NBIAPI.config.isInClaudeCodeMode &&
|
|
1469
|
+
NBIAPI.config.usingGitHubCopilotModel
|
|
1467
1470
|
});
|
|
1468
1471
|
NBIAPI.configChanged.connect(() => {
|
|
1469
|
-
if (NBIAPI.config.
|
|
1472
|
+
if (!NBIAPI.config.isInClaudeCodeMode &&
|
|
1473
|
+
NBIAPI.config.usingGitHubCopilotModel) {
|
|
1470
1474
|
githubCopilotStatusBarItem.show();
|
|
1471
1475
|
}
|
|
1472
1476
|
else {
|
package/lib/tokens.d.ts
CHANGED
|
@@ -27,7 +27,8 @@ export declare enum BackendMessageType {
|
|
|
27
27
|
StreamEnd = "stream-end",
|
|
28
28
|
RunUICommand = "run-ui-command",
|
|
29
29
|
GitHubCopilotLoginStatusChange = "github-copilot-login-status-change",
|
|
30
|
-
MCPServerStatusChange = "mcp-server-status-change"
|
|
30
|
+
MCPServerStatusChange = "mcp-server-status-change",
|
|
31
|
+
ClaudeCodeStatusChange = "claude-code-status-change"
|
|
31
32
|
}
|
|
32
33
|
export declare enum ResponseStreamDataType {
|
|
33
34
|
LLMRaw = "llm-raw",
|
|
@@ -38,7 +39,8 @@ export declare enum ResponseStreamDataType {
|
|
|
38
39
|
Button = "button",
|
|
39
40
|
Anchor = "anchor",
|
|
40
41
|
Progress = "progress",
|
|
41
|
-
Confirmation = "confirmation"
|
|
42
|
+
Confirmation = "confirmation",
|
|
43
|
+
AskUserQuestion = "ask-user-question"
|
|
42
44
|
}
|
|
43
45
|
export declare enum ContextType {
|
|
44
46
|
Custom = "custom",
|
|
@@ -93,6 +95,7 @@ export declare enum BuiltinToolsetType {
|
|
|
93
95
|
CommandExecute = "nbi-command-execute"
|
|
94
96
|
}
|
|
95
97
|
export declare const GITHUB_COPILOT_PROVIDER_ID = "github-copilot";
|
|
98
|
+
export declare const CLAUDE_CODE_CHAT_PARTICIPANT_ID = "claude-code";
|
|
96
99
|
export declare enum TelemetryEventType {
|
|
97
100
|
InlineCompletionRequest = "inline-completion-request",
|
|
98
101
|
ExplainThisRequest = "explain-this-request",
|