@plmbr/notebook-intelligence 5.0.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/LICENSE +674 -0
- package/README.md +412 -0
- package/lib/api.d.ts +288 -0
- package/lib/api.js +927 -0
- package/lib/cell-output-bundle.d.ts +25 -0
- package/lib/cell-output-bundle.js +129 -0
- package/lib/cell-output-toolbar.d.ts +26 -0
- package/lib/cell-output-toolbar.js +188 -0
- package/lib/chat-progress-feedback.d.ts +3 -0
- package/lib/chat-progress-feedback.js +27 -0
- package/lib/chat-sidebar.d.ts +92 -0
- package/lib/chat-sidebar.js +3452 -0
- package/lib/command-ids.d.ts +39 -0
- package/lib/command-ids.js +44 -0
- package/lib/components/ask-user-question.d.ts +2 -0
- package/lib/components/ask-user-question.js +85 -0
- package/lib/components/checkbox.d.ts +2 -0
- package/lib/components/checkbox.js +30 -0
- package/lib/components/claude-mcp-panel.d.ts +2 -0
- package/lib/components/claude-mcp-panel.js +275 -0
- package/lib/components/claude-mcp-paste.d.ts +7 -0
- package/lib/components/claude-mcp-paste.js +104 -0
- package/lib/components/claude-session-picker.d.ts +8 -0
- package/lib/components/claude-session-picker.js +127 -0
- package/lib/components/form-dialog.d.ts +25 -0
- package/lib/components/form-dialog.js +35 -0
- package/lib/components/launcher-picker.d.ts +6 -0
- package/lib/components/launcher-picker.js +135 -0
- package/lib/components/mcp-util.d.ts +2 -0
- package/lib/components/mcp-util.js +37 -0
- package/lib/components/notebook-generation-popover.d.ts +7 -0
- package/lib/components/notebook-generation-popover.js +60 -0
- package/lib/components/pill.d.ts +2 -0
- package/lib/components/pill.js +5 -0
- package/lib/components/plugins-panel.d.ts +3 -0
- package/lib/components/plugins-panel.js +466 -0
- package/lib/components/settings-panel.d.ts +11 -0
- package/lib/components/settings-panel.js +742 -0
- package/lib/components/skills-panel.d.ts +2 -0
- package/lib/components/skills-panel.js +1264 -0
- package/lib/handler.d.ts +8 -0
- package/lib/handler.js +36 -0
- package/lib/icons.d.ts +45 -0
- package/lib/icons.js +54 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +2079 -0
- package/lib/markdown-renderer.d.ts +10 -0
- package/lib/markdown-renderer.js +64 -0
- package/lib/notebook-generation-toolbar.d.ts +16 -0
- package/lib/notebook-generation-toolbar.js +197 -0
- package/lib/notebook-generation.d.ts +8 -0
- package/lib/notebook-generation.js +12 -0
- package/lib/open-file-refresh-watcher-env.d.ts +4 -0
- package/lib/open-file-refresh-watcher-env.js +33 -0
- package/lib/open-file-refresh-watcher.d.ts +97 -0
- package/lib/open-file-refresh-watcher.js +190 -0
- package/lib/shell-utils.d.ts +6 -0
- package/lib/shell-utils.js +9 -0
- package/lib/task-target-notebook.d.ts +2 -0
- package/lib/task-target-notebook.js +28 -0
- package/lib/terminal-drag-format.d.ts +9 -0
- package/lib/terminal-drag-format.js +23 -0
- package/lib/terminal-drag.d.ts +12 -0
- package/lib/terminal-drag.js +268 -0
- package/lib/tokens.d.ts +149 -0
- package/lib/tokens.js +88 -0
- package/lib/tour/tour-anchors.d.ts +18 -0
- package/lib/tour/tour-anchors.js +18 -0
- package/lib/tour/tour-config.d.ts +66 -0
- package/lib/tour/tour-config.js +99 -0
- package/lib/tour/tour-defaults.json +58 -0
- package/lib/tour/tour-events.d.ts +19 -0
- package/lib/tour/tour-events.js +30 -0
- package/lib/tour/tour-overlay.d.ts +6 -0
- package/lib/tour/tour-overlay.js +350 -0
- package/lib/tour/tour-state.d.ts +20 -0
- package/lib/tour/tour-state.js +81 -0
- package/lib/tour/tour-steps.d.ts +33 -0
- package/lib/tour/tour-steps.js +216 -0
- package/lib/utils.d.ts +53 -0
- package/lib/utils.js +385 -0
- package/package.json +258 -0
- package/schema/plugin.json +42 -0
- package/src/api.ts +1424 -0
- package/src/cell-output-bundle.ts +176 -0
- package/src/cell-output-toolbar.ts +232 -0
- package/src/chat-progress-feedback.ts +35 -0
- package/src/chat-sidebar.tsx +5147 -0
- package/src/command-ids.ts +67 -0
- package/src/components/ask-user-question.tsx +151 -0
- package/src/components/checkbox.tsx +62 -0
- package/src/components/claude-mcp-panel.tsx +543 -0
- package/src/components/claude-mcp-paste.ts +132 -0
- package/src/components/claude-session-picker.tsx +214 -0
- package/src/components/form-dialog.tsx +75 -0
- package/src/components/launcher-picker.tsx +237 -0
- package/src/components/mcp-util.ts +53 -0
- package/src/components/notebook-generation-popover.tsx +127 -0
- package/src/components/pill.tsx +15 -0
- package/src/components/plugins-panel.tsx +774 -0
- package/src/components/settings-panel.tsx +1631 -0
- package/src/components/skills-panel.tsx +2084 -0
- package/src/handler.ts +51 -0
- package/src/icons.ts +71 -0
- package/src/index.ts +2583 -0
- package/src/markdown-renderer.tsx +153 -0
- package/src/notebook-generation-toolbar.tsx +281 -0
- package/src/notebook-generation.ts +23 -0
- package/src/open-file-refresh-watcher-env.ts +52 -0
- package/src/open-file-refresh-watcher.ts +260 -0
- package/src/shell-utils.ts +10 -0
- package/src/svg.d.ts +4 -0
- package/src/task-target-notebook.ts +37 -0
- package/src/terminal-drag-format.ts +29 -0
- package/src/terminal-drag.ts +382 -0
- package/src/tokens.ts +171 -0
- package/src/tour/tour-anchors.ts +21 -0
- package/src/tour/tour-config.ts +160 -0
- package/src/tour/tour-events.ts +34 -0
- package/src/tour/tour-overlay.tsx +474 -0
- package/src/tour/tour-state.ts +87 -0
- package/src/tour/tour-steps.ts +281 -0
- package/src/utils.ts +455 -0
- package/style/base.css +3238 -0
- package/style/icons/cell-toolbar-bug.svg +5 -0
- package/style/icons/cell-toolbar-chat.svg +5 -0
- package/style/icons/cell-toolbar-sparkle.svg +5 -0
- package/style/icons/claude.svg +1 -0
- package/style/icons/copilot-warning.svg +1 -0
- package/style/icons/copilot.svg +1 -0
- package/style/icons/copy.svg +1 -0
- package/style/icons/openai.svg +1 -0
- package/style/icons/opencode.svg +1 -0
- package/style/icons/sparkles-warning.svg +5 -0
- package/style/icons/sparkles.svg +1 -0
- package/style/index.css +1 -0
- package/style/index.js +1 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
// Copyright (c) Mehmet Bektas <mbektasgh@outlook.com>
|
|
2
|
+
import React, { useEffect, useState } from 'react';
|
|
3
|
+
import { Dialog, showDialog } from '@jupyterlab/apputils';
|
|
4
|
+
import { NBIAPI } from '../api';
|
|
5
|
+
import { FormDialog } from './form-dialog';
|
|
6
|
+
const SCOPES = ['user', 'project', 'local'];
|
|
7
|
+
const SCOPE_HINT = {
|
|
8
|
+
user: 'available in all your projects',
|
|
9
|
+
project: 'shared via the project repo',
|
|
10
|
+
local: 'this project, this user only'
|
|
11
|
+
};
|
|
12
|
+
function marketplaceName(marketplace) {
|
|
13
|
+
var _a;
|
|
14
|
+
return String((_a = marketplace.name) !== null && _a !== void 0 ? _a : '').trim();
|
|
15
|
+
}
|
|
16
|
+
// Plugin-name list trimmed with ellipsis. Caller decides the visible cap;
|
|
17
|
+
// the helper just stitches a comma-separated string and appends the
|
|
18
|
+
// "+N more" tail when the cap is hit, so the row width stays bounded
|
|
19
|
+
// even for marketplaces with hundreds of plugins.
|
|
20
|
+
export function summarizePluginNames(names, visible) {
|
|
21
|
+
if (!names || names.length === 0) {
|
|
22
|
+
return '';
|
|
23
|
+
}
|
|
24
|
+
if (names.length <= visible) {
|
|
25
|
+
return names.join(', ');
|
|
26
|
+
}
|
|
27
|
+
const head = names.slice(0, visible).join(', ');
|
|
28
|
+
const remaining = names.length - visible;
|
|
29
|
+
return `${head}, +${remaining} more`;
|
|
30
|
+
}
|
|
31
|
+
function pluginName(plugin) {
|
|
32
|
+
var _a, _b;
|
|
33
|
+
return String((_b = (_a = plugin.name) !== null && _a !== void 0 ? _a : plugin.id) !== null && _b !== void 0 ? _b : '').trim();
|
|
34
|
+
}
|
|
35
|
+
function pluginEntryLabel(plugin) {
|
|
36
|
+
const name = pluginName(plugin);
|
|
37
|
+
const description = typeof plugin.description === 'string' ? plugin.description : '';
|
|
38
|
+
return description ? `${name} - ${description}` : name;
|
|
39
|
+
}
|
|
40
|
+
export function SettingsPanelComponentPlugins(_props) {
|
|
41
|
+
var _a, _b;
|
|
42
|
+
const [plugins, setPlugins] = useState([]);
|
|
43
|
+
const [marketplaces, setMarketplaces] = useState([]);
|
|
44
|
+
const [loading, setLoading] = useState(true);
|
|
45
|
+
const [error, setError] = useState(null);
|
|
46
|
+
const [installOpen, setInstallOpen] = useState(false);
|
|
47
|
+
const [marketplaceOpen, setMarketplaceOpen] = useState(false);
|
|
48
|
+
// Composite key (`scope:name`) so a user-scope plugin and a project-scope
|
|
49
|
+
// plugin sharing a name don't clobber each other's busy indicators.
|
|
50
|
+
const [busyPluginKey, setBusyPluginKey] = useState(null);
|
|
51
|
+
const [busyMarketplace, setBusyMarketplace] = useState(null);
|
|
52
|
+
const [allowGithubImport, setAllowGithubImport] = useState(NBIAPI.config.allowGithubPluginImport);
|
|
53
|
+
const refresh = async () => {
|
|
54
|
+
var _a;
|
|
55
|
+
setLoading(true);
|
|
56
|
+
setError(null);
|
|
57
|
+
try {
|
|
58
|
+
const [p, m] = await Promise.all([
|
|
59
|
+
NBIAPI.listPlugins(),
|
|
60
|
+
NBIAPI.listPluginMarketplaces()
|
|
61
|
+
]);
|
|
62
|
+
setPlugins(p);
|
|
63
|
+
setMarketplaces(m);
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
setError((_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : String(e));
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
setLoading(false);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
refresh();
|
|
74
|
+
const handler = () => {
|
|
75
|
+
setAllowGithubImport(NBIAPI.config.allowGithubPluginImport);
|
|
76
|
+
};
|
|
77
|
+
NBIAPI.configChanged.connect(handler);
|
|
78
|
+
return () => {
|
|
79
|
+
NBIAPI.configChanged.disconnect(handler);
|
|
80
|
+
};
|
|
81
|
+
}, []);
|
|
82
|
+
const handleUninstall = async (p) => {
|
|
83
|
+
var _a, _b;
|
|
84
|
+
const name = pluginName(p);
|
|
85
|
+
const scope = (_a = p.scope) !== null && _a !== void 0 ? _a : 'user';
|
|
86
|
+
if (!name) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const ok = await showDialog({
|
|
90
|
+
title: 'Uninstall plugin?',
|
|
91
|
+
body: `"${name}" will be removed from Claude's ${scope}-scope config.`,
|
|
92
|
+
buttons: [
|
|
93
|
+
Dialog.cancelButton(),
|
|
94
|
+
Dialog.warnButton({ label: 'Uninstall' })
|
|
95
|
+
]
|
|
96
|
+
});
|
|
97
|
+
if (!ok.button.accept) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const busyKey = `${scope}:${name}`;
|
|
101
|
+
setBusyPluginKey(busyKey);
|
|
102
|
+
try {
|
|
103
|
+
await NBIAPI.uninstallPlugin(name, scope);
|
|
104
|
+
await refresh();
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
setError(`Failed to uninstall: ${(_b = e === null || e === void 0 ? void 0 : e.message) !== null && _b !== void 0 ? _b : e}`);
|
|
108
|
+
}
|
|
109
|
+
finally {
|
|
110
|
+
setBusyPluginKey(null);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const handleToggleEnabled = async (p) => {
|
|
114
|
+
var _a, _b;
|
|
115
|
+
const name = pluginName(p);
|
|
116
|
+
const scope = (_a = p.scope) !== null && _a !== void 0 ? _a : 'user';
|
|
117
|
+
if (!name) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const busyKey = `${scope}:${name}`;
|
|
121
|
+
setBusyPluginKey(busyKey);
|
|
122
|
+
try {
|
|
123
|
+
await NBIAPI.setPluginEnabled(name, scope, !p.enabled);
|
|
124
|
+
await refresh();
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
setError(`Failed to update: ${(_b = e === null || e === void 0 ? void 0 : e.message) !== null && _b !== void 0 ? _b : e}`);
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
setBusyPluginKey(null);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
const handleRemoveMarketplace = async (m) => {
|
|
134
|
+
var _a, _b;
|
|
135
|
+
const name = String((_a = m.name) !== null && _a !== void 0 ? _a : '');
|
|
136
|
+
if (!name) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const ok = await showDialog({
|
|
140
|
+
title: 'Remove marketplace?',
|
|
141
|
+
body: `"${name}" will be removed from Claude's plugin marketplaces.`,
|
|
142
|
+
buttons: [Dialog.cancelButton(), Dialog.warnButton({ label: 'Remove' })]
|
|
143
|
+
});
|
|
144
|
+
if (!ok.button.accept) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
setBusyMarketplace(name);
|
|
148
|
+
try {
|
|
149
|
+
await NBIAPI.removePluginMarketplace(name);
|
|
150
|
+
await refresh();
|
|
151
|
+
}
|
|
152
|
+
catch (e) {
|
|
153
|
+
setError(`Failed to remove: ${(_b = e === null || e === void 0 ? void 0 : e.message) !== null && _b !== void 0 ? _b : e}`);
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
setBusyMarketplace(null);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
const handleUpdateMarketplace = async (m) => {
|
|
160
|
+
var _a, _b;
|
|
161
|
+
const name = String((_a = m.name) !== null && _a !== void 0 ? _a : '');
|
|
162
|
+
if (!name) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
setBusyMarketplace(name);
|
|
166
|
+
try {
|
|
167
|
+
await NBIAPI.updatePluginMarketplace(name);
|
|
168
|
+
await refresh();
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
setError(`Failed to update marketplace: ${(_b = e === null || e === void 0 ? void 0 : e.message) !== null && _b !== void 0 ? _b : e}`);
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
setBusyMarketplace(null);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
const grouped = {
|
|
178
|
+
user: [],
|
|
179
|
+
project: [],
|
|
180
|
+
local: []
|
|
181
|
+
};
|
|
182
|
+
for (const p of plugins) {
|
|
183
|
+
const scope = (_a = p.scope) !== null && _a !== void 0 ? _a : 'user';
|
|
184
|
+
((_b = grouped[scope]) !== null && _b !== void 0 ? _b : grouped.user).push(p);
|
|
185
|
+
}
|
|
186
|
+
return (React.createElement("div", { className: "config-dialog-body nbi-skills-panel" },
|
|
187
|
+
React.createElement("div", { className: "nbi-skills-header" },
|
|
188
|
+
React.createElement("div", { className: "nbi-skills-title" }, "Plugins"),
|
|
189
|
+
React.createElement("div", { className: "nbi-skills-header-actions" },
|
|
190
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-reject jp-mod-styled", onClick: refresh, disabled: loading },
|
|
191
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, loading ? 'Refreshing…' : 'Refresh')),
|
|
192
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-reject jp-mod-styled", onClick: () => setMarketplaceOpen(true) },
|
|
193
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Add marketplace")),
|
|
194
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: () => setInstallOpen(true) },
|
|
195
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, "Install plugin")))),
|
|
196
|
+
React.createElement("div", { className: "nbi-info-banner", role: "note" }, "Add a marketplace to discover plugins, then install one to extend Claude Code with new commands, agents, and tool integrations."),
|
|
197
|
+
error && (React.createElement("div", { className: "nbi-skills-error", role: "alert" }, error)),
|
|
198
|
+
React.createElement("div", { className: "nbi-skills-section" },
|
|
199
|
+
React.createElement("div", { className: "nbi-skills-section-caption" }, "MARKETPLACES"),
|
|
200
|
+
marketplaces.length === 0 ? (React.createElement("div", { className: "nbi-skills-empty" }, loading
|
|
201
|
+
? 'Loading…'
|
|
202
|
+
: 'No marketplaces configured. Add one to discover plugins.')) : (marketplaces.map((m, i) => {
|
|
203
|
+
var _a, _b, _c;
|
|
204
|
+
return (React.createElement(MarketplaceRow, { key: String((_b = (_a = m.name) !== null && _a !== void 0 ? _a : m.source) !== null && _b !== void 0 ? _b : i), info: m, busy: busyMarketplace === String((_c = m.name) !== null && _c !== void 0 ? _c : ''), onRemove: () => handleRemoveMarketplace(m), onUpdate: () => handleUpdateMarketplace(m) }));
|
|
205
|
+
}))),
|
|
206
|
+
SCOPES.map(scope => (React.createElement(PluginScopeSection, { key: scope, scope: scope, plugins: grouped[scope], loading: loading, busyPluginKey: busyPluginKey, onUninstall: handleUninstall, onToggle: handleToggleEnabled }))),
|
|
207
|
+
installOpen && (React.createElement(PluginInstallDialog, { marketplaces: marketplaces, onCancel: () => setInstallOpen(false), onSubmit: async ({ plugin, scope }) => {
|
|
208
|
+
await NBIAPI.installPlugin(plugin, scope);
|
|
209
|
+
setInstallOpen(false);
|
|
210
|
+
await refresh();
|
|
211
|
+
} })),
|
|
212
|
+
marketplaceOpen && (React.createElement(MarketplaceAddDialog, { allowGithubImport: allowGithubImport, onCancel: () => setMarketplaceOpen(false), onSubmit: async ({ source, scope }) => {
|
|
213
|
+
await NBIAPI.addPluginMarketplace(source, scope);
|
|
214
|
+
setMarketplaceOpen(false);
|
|
215
|
+
await refresh();
|
|
216
|
+
} }))));
|
|
217
|
+
}
|
|
218
|
+
function PluginScopeSection(props) {
|
|
219
|
+
return (React.createElement("div", { className: "nbi-skills-section" },
|
|
220
|
+
React.createElement("div", { className: "nbi-skills-section-caption", title: SCOPE_HINT[props.scope] }, props.scope.toUpperCase()),
|
|
221
|
+
props.plugins.length === 0 ? (React.createElement("div", { className: "nbi-skills-empty" }, props.loading ? 'Loading…' : 'No plugins in this scope.')) : (props.plugins.map(p => {
|
|
222
|
+
var _a;
|
|
223
|
+
const scope = (_a = p.scope) !== null && _a !== void 0 ? _a : props.scope;
|
|
224
|
+
const name = pluginName(p);
|
|
225
|
+
const rowKey = `${scope}:${name}`;
|
|
226
|
+
return (React.createElement(PluginRow, { key: rowKey, plugin: p, busy: props.busyPluginKey === rowKey, onUninstall: () => props.onUninstall(p), onToggle: () => props.onToggle(p) }));
|
|
227
|
+
}))));
|
|
228
|
+
}
|
|
229
|
+
function PluginRow(props) {
|
|
230
|
+
const { plugin } = props;
|
|
231
|
+
const description = [plugin.version, plugin.marketplace, plugin.description]
|
|
232
|
+
.filter(Boolean)
|
|
233
|
+
.map(String)
|
|
234
|
+
.join(' · ');
|
|
235
|
+
const enabled = plugin.enabled !== false;
|
|
236
|
+
return (React.createElement("div", { className: "nbi-skill-row" },
|
|
237
|
+
React.createElement("div", { className: "nbi-skill-row-main" },
|
|
238
|
+
React.createElement("div", { className: "nbi-skill-row-name" },
|
|
239
|
+
pluginName(plugin) || '(unnamed)',
|
|
240
|
+
!enabled && React.createElement("span", null, " \u2014 disabled")),
|
|
241
|
+
description && (React.createElement("div", { className: "nbi-skill-row-description" }, description))),
|
|
242
|
+
React.createElement("div", { className: "nbi-skill-row-actions", onClick: e => e.stopPropagation() },
|
|
243
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-reject jp-mod-styled", onClick: props.onToggle, disabled: props.busy },
|
|
244
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, enabled ? 'Disable' : 'Enable')),
|
|
245
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-reject jp-mod-styled", onClick: props.onUninstall, disabled: props.busy },
|
|
246
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, props.busy ? 'Working…' : 'Uninstall')))));
|
|
247
|
+
}
|
|
248
|
+
// Visible plugin-name cap on the marketplace row. Picked so a typical
|
|
249
|
+
// marketplace (a handful of plugins) fits on one line, with longer lists
|
|
250
|
+
// collapsing to "a, b, c, +N more" so the row width stays bounded.
|
|
251
|
+
const MARKETPLACE_VISIBLE_PLUGINS = 5;
|
|
252
|
+
function MarketplaceRow(props) {
|
|
253
|
+
var _a;
|
|
254
|
+
const { info } = props;
|
|
255
|
+
const description = typeof info.description === 'string' ? info.description.trim() : '';
|
|
256
|
+
const version = typeof info.version === 'string' ? info.version.trim() : '';
|
|
257
|
+
const pluginNames = Array.isArray(info.plugin_names)
|
|
258
|
+
? info.plugin_names.filter((n) => typeof n === 'string')
|
|
259
|
+
: [];
|
|
260
|
+
const pluginCount = typeof info.plugin_count === 'number'
|
|
261
|
+
? info.plugin_count
|
|
262
|
+
: pluginNames.length;
|
|
263
|
+
const pluginSummary = summarizePluginNames(pluginNames, MARKETPLACE_VISIBLE_PLUGINS);
|
|
264
|
+
// Pluralize for 0/1/N: "no plugins" reads better than "0 plugins" when
|
|
265
|
+
// the marketplace manifest has not yet been refreshed and we have no
|
|
266
|
+
// plugin entries.
|
|
267
|
+
const pluginCountLabel = pluginCount === 0
|
|
268
|
+
? 'no plugins'
|
|
269
|
+
: pluginCount === 1
|
|
270
|
+
? '1 plugin'
|
|
271
|
+
: `${pluginCount} plugins`;
|
|
272
|
+
// Only render the plugin summary line when the manifest read produced
|
|
273
|
+
// a count or name list. Without this guard, a marketplace that the
|
|
274
|
+
// user just added but whose manifest hasn't been cached yet would
|
|
275
|
+
// render "no plugins" and look broken; here it just renders no line
|
|
276
|
+
// at all until the next refresh picks up the manifest.
|
|
277
|
+
const hasPluginManifestData = Array.isArray(info.plugin_names) || typeof info.plugin_count === 'number';
|
|
278
|
+
return (React.createElement("div", { className: "nbi-skill-row" },
|
|
279
|
+
React.createElement("div", { className: "nbi-skill-row-main" },
|
|
280
|
+
React.createElement("div", { className: "nbi-skill-row-name" },
|
|
281
|
+
String((_a = info.name) !== null && _a !== void 0 ? _a : '(unnamed)'),
|
|
282
|
+
version && (React.createElement("span", { className: "nbi-skill-row-version" },
|
|
283
|
+
" v",
|
|
284
|
+
version))),
|
|
285
|
+
description && (React.createElement("div", { className: "nbi-skill-row-description" }, description)),
|
|
286
|
+
info.source && (React.createElement("div", { className: "nbi-skill-row-description" },
|
|
287
|
+
React.createElement("code", null, String(info.source)))),
|
|
288
|
+
hasPluginManifestData && (React.createElement("div", { className: "nbi-skill-row-description nbi-skill-row-plugins" },
|
|
289
|
+
pluginCountLabel,
|
|
290
|
+
pluginSummary && React.createElement("span", null, `: ${pluginSummary}`)))),
|
|
291
|
+
React.createElement("div", { className: "nbi-skill-row-actions", onClick: e => e.stopPropagation() },
|
|
292
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-accept jp-mod-styled", onClick: props.onUpdate, disabled: props.busy, title: "Refresh this marketplace from its source" },
|
|
293
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, props.busy ? 'Updating…' : 'Update')),
|
|
294
|
+
React.createElement("button", { className: "jp-Dialog-button jp-mod-reject jp-mod-styled", onClick: props.onRemove, disabled: props.busy },
|
|
295
|
+
React.createElement("div", { className: "jp-Dialog-buttonLabel" }, props.busy ? 'Removing…' : 'Remove')))));
|
|
296
|
+
}
|
|
297
|
+
function PluginInstallDialog(props) {
|
|
298
|
+
var _a;
|
|
299
|
+
const marketplaceNames = props.marketplaces
|
|
300
|
+
.map(marketplaceName)
|
|
301
|
+
.filter(Boolean);
|
|
302
|
+
const marketplaceKey = marketplaceNames.join('\n');
|
|
303
|
+
const [installMode, setInstallMode] = useState(marketplaceNames.length > 0 ? 'marketplace' : 'manual');
|
|
304
|
+
const [marketplace, setMarketplace] = useState((_a = marketplaceNames[0]) !== null && _a !== void 0 ? _a : '');
|
|
305
|
+
const [marketplacePlugins, setMarketplacePlugins] = useState([]);
|
|
306
|
+
const [selectedPlugin, setSelectedPlugin] = useState('');
|
|
307
|
+
const [manualPlugin, setManualPlugin] = useState('');
|
|
308
|
+
const [scope, setScope] = useState('user');
|
|
309
|
+
const [loadingPlugins, setLoadingPlugins] = useState(false);
|
|
310
|
+
const [pluginListError, setPluginListError] = useState(null);
|
|
311
|
+
const [submitting, setSubmitting] = useState(false);
|
|
312
|
+
const [submitError, setSubmitError] = useState(null);
|
|
313
|
+
useEffect(() => {
|
|
314
|
+
var _a;
|
|
315
|
+
if (marketplace && marketplaceNames.includes(marketplace)) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
setMarketplace((_a = marketplaceNames[0]) !== null && _a !== void 0 ? _a : '');
|
|
319
|
+
}, [marketplace, marketplaceKey]);
|
|
320
|
+
useEffect(() => {
|
|
321
|
+
let cancelled = false;
|
|
322
|
+
if (installMode !== 'marketplace') {
|
|
323
|
+
setLoadingPlugins(false);
|
|
324
|
+
setPluginListError(null);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (!marketplace) {
|
|
328
|
+
setMarketplacePlugins([]);
|
|
329
|
+
setSelectedPlugin('');
|
|
330
|
+
setPluginListError(null);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
setLoadingPlugins(true);
|
|
334
|
+
setPluginListError(null);
|
|
335
|
+
NBIAPI.listPluginMarketplacePlugins(marketplace)
|
|
336
|
+
.then(plugins => {
|
|
337
|
+
if (cancelled) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
const namedPlugins = plugins.filter(plugin => pluginName(plugin));
|
|
341
|
+
setMarketplacePlugins(namedPlugins);
|
|
342
|
+
const firstPluginName = namedPlugins.length > 0 ? pluginName(namedPlugins[0]) : '';
|
|
343
|
+
setSelectedPlugin(current => namedPlugins.some(plugin => pluginName(plugin) === current)
|
|
344
|
+
? current
|
|
345
|
+
: firstPluginName);
|
|
346
|
+
})
|
|
347
|
+
.catch((e) => {
|
|
348
|
+
var _a;
|
|
349
|
+
if (cancelled) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
setMarketplacePlugins([]);
|
|
353
|
+
setSelectedPlugin('');
|
|
354
|
+
setPluginListError(`Failed to load plugins: ${(_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e}`);
|
|
355
|
+
})
|
|
356
|
+
.finally(() => {
|
|
357
|
+
if (!cancelled) {
|
|
358
|
+
setLoadingPlugins(false);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
return () => {
|
|
362
|
+
cancelled = true;
|
|
363
|
+
};
|
|
364
|
+
}, [installMode, marketplace]);
|
|
365
|
+
const trimmedManualPlugin = manualPlugin.trim();
|
|
366
|
+
const canSubmit = !submitting &&
|
|
367
|
+
(installMode === 'manual'
|
|
368
|
+
? Boolean(trimmedManualPlugin)
|
|
369
|
+
: Boolean(marketplace && selectedPlugin && !loadingPlugins));
|
|
370
|
+
const handleSubmit = async () => {
|
|
371
|
+
var _a;
|
|
372
|
+
if (!canSubmit) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
setSubmitError(null);
|
|
376
|
+
setSubmitting(true);
|
|
377
|
+
try {
|
|
378
|
+
await props.onSubmit({
|
|
379
|
+
plugin: installMode === 'manual'
|
|
380
|
+
? trimmedManualPlugin
|
|
381
|
+
: `${selectedPlugin}@${marketplace}`,
|
|
382
|
+
scope
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
catch (e) {
|
|
386
|
+
setSubmitError((_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : String(e));
|
|
387
|
+
}
|
|
388
|
+
finally {
|
|
389
|
+
setSubmitting(false);
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
return (React.createElement(FormDialog, { title: "Install plugin", submitLabel: "Install", submitInProgressLabel: "Installing\u2026", canSubmit: Boolean(canSubmit), submitting: submitting, error: submitError, onCancel: props.onCancel, onSubmit: handleSubmit },
|
|
393
|
+
React.createElement("div", { className: "nbi-form-field" },
|
|
394
|
+
React.createElement("label", null, "Install from"),
|
|
395
|
+
React.createElement("select", { value: installMode, onChange: e => setInstallMode(e.target.value), disabled: submitting, autoFocus: true },
|
|
396
|
+
React.createElement("option", { value: "marketplace", disabled: marketplaceNames.length === 0 }, "Marketplace picker"),
|
|
397
|
+
React.createElement("option", { value: "manual" }, "Specify manually"))),
|
|
398
|
+
installMode === 'manual' ? (React.createElement(React.Fragment, null,
|
|
399
|
+
React.createElement("div", { className: "nbi-form-field" },
|
|
400
|
+
React.createElement("label", null, "Plugin"),
|
|
401
|
+
React.createElement("input", { type: "text", value: manualPlugin, onChange: e => setManualPlugin(e.target.value), placeholder: "plugin@marketplace", disabled: submitting })),
|
|
402
|
+
React.createElement("div", { className: "nbi-form-hint" }, "Enter a plugin name or the plugin@marketplace shorthand."))) : (React.createElement(React.Fragment, null,
|
|
403
|
+
React.createElement("div", { className: "nbi-form-field" },
|
|
404
|
+
React.createElement("label", null, "Marketplace"),
|
|
405
|
+
React.createElement("select", { value: marketplace, onChange: e => setMarketplace(e.target.value), disabled: submitting }, marketplaceNames.map(name => (React.createElement("option", { key: name, value: name }, name))))),
|
|
406
|
+
React.createElement("div", { className: "nbi-form-field" },
|
|
407
|
+
React.createElement("label", null, "Plugin"),
|
|
408
|
+
React.createElement("select", { value: selectedPlugin, onChange: e => setSelectedPlugin(e.target.value), disabled: submitting || loadingPlugins || marketplacePlugins.length === 0 }, marketplacePlugins.map(plugin => {
|
|
409
|
+
const name = pluginName(plugin);
|
|
410
|
+
return (React.createElement("option", { key: name, value: name }, pluginEntryLabel(plugin)));
|
|
411
|
+
}))),
|
|
412
|
+
loadingPlugins && (React.createElement("div", { className: "nbi-form-hint" }, "Loading marketplace plugins...")),
|
|
413
|
+
!loadingPlugins &&
|
|
414
|
+
!pluginListError &&
|
|
415
|
+
marketplacePlugins.length === 0 && (React.createElement("div", { className: "nbi-form-hint" }, "No plugins were found in this marketplace.")),
|
|
416
|
+
pluginListError && (React.createElement("div", { className: "nbi-skills-error", role: "alert" }, pluginListError)))),
|
|
417
|
+
React.createElement("div", { className: "nbi-form-field" },
|
|
418
|
+
React.createElement("label", null, "Scope"),
|
|
419
|
+
React.createElement("select", { value: scope, onChange: e => setScope(e.target.value) }, SCOPES.map(s => (React.createElement("option", { key: s, value: s },
|
|
420
|
+
s,
|
|
421
|
+
" \u2014 ",
|
|
422
|
+
SCOPE_HINT[s])))))));
|
|
423
|
+
}
|
|
424
|
+
function MarketplaceAddDialog(props) {
|
|
425
|
+
const [source, setSource] = useState('');
|
|
426
|
+
const [scope, setScope] = useState('user');
|
|
427
|
+
const [submitting, setSubmitting] = useState(false);
|
|
428
|
+
const [submitError, setSubmitError] = useState(null);
|
|
429
|
+
const canSubmit = source.trim() && !submitting;
|
|
430
|
+
const handleSubmit = async () => {
|
|
431
|
+
var _a;
|
|
432
|
+
if (!canSubmit) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
setSubmitError(null);
|
|
436
|
+
setSubmitting(true);
|
|
437
|
+
try {
|
|
438
|
+
await props.onSubmit({ source: source.trim(), scope });
|
|
439
|
+
}
|
|
440
|
+
catch (e) {
|
|
441
|
+
setSubmitError((_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : String(e));
|
|
442
|
+
}
|
|
443
|
+
finally {
|
|
444
|
+
setSubmitting(false);
|
|
445
|
+
}
|
|
446
|
+
};
|
|
447
|
+
return (React.createElement(FormDialog, { title: "Add plugin marketplace", submitLabel: "Add", submitInProgressLabel: "Adding\u2026", canSubmit: Boolean(canSubmit), submitting: submitting, error: submitError, onCancel: props.onCancel, onSubmit: handleSubmit },
|
|
448
|
+
React.createElement("div", { className: "nbi-form-field" },
|
|
449
|
+
React.createElement("label", null, "Source"),
|
|
450
|
+
React.createElement("input", { type: "text", value: source, onChange: e => setSource(e.target.value), placeholder: props.allowGithubImport
|
|
451
|
+
? 'owner/repo, https://github.com/owner/repo, or local path'
|
|
452
|
+
: 'https://… or local path', autoFocus: true })),
|
|
453
|
+
props.allowGithubImport ? (React.createElement("div", { className: "nbi-form-hint" },
|
|
454
|
+
"Private GitHub sources work if ",
|
|
455
|
+
React.createElement("code", null, "GITHUB_TOKEN"),
|
|
456
|
+
" is set or",
|
|
457
|
+
' ',
|
|
458
|
+
React.createElement("code", null, "gh auth login"),
|
|
459
|
+
" is configured on the Jupyter server.")) : (React.createElement("div", { className: "nbi-form-hint" }, "GitHub-sourced marketplaces are disabled by your administrator. Use a non-GitHub URL or a local filesystem path.")),
|
|
460
|
+
React.createElement("div", { className: "nbi-form-field" },
|
|
461
|
+
React.createElement("label", null, "Scope"),
|
|
462
|
+
React.createElement("select", { value: scope, onChange: e => setScope(e.target.value) }, SCOPES.map(s => (React.createElement("option", { key: s, value: s },
|
|
463
|
+
s,
|
|
464
|
+
" \u2014 ",
|
|
465
|
+
SCOPE_HINT[s])))))));
|
|
466
|
+
}
|
|
@@ -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
|
+
}
|