@jupyterlab/extensionmanager 4.0.0-alpha.13 → 4.0.0-alpha.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +0 -2
- package/lib/index.js +0 -2
- package/lib/index.js.map +1 -1
- package/lib/model.d.ts +81 -194
- package/lib/model.js +191 -455
- package/lib/model.js.map +1 -1
- package/lib/widget.d.ts +18 -207
- package/lib/widget.js +189 -315
- package/lib/widget.js.map +1 -1
- package/package.json +9 -11
- package/style/base.css +40 -98
- package/style/index.css +1 -1
- package/style/index.js +1 -1
- package/lib/build-helper.d.ts +0 -9
- package/lib/build-helper.js +0 -60
- package/lib/build-helper.js.map +0 -1
- package/lib/companions.d.ts +0 -88
- package/lib/companions.js +0 -98
- package/lib/companions.js.map +0 -1
- package/lib/listings.d.ts +0 -50
- package/lib/listings.js +0 -80
- package/lib/listings.js.map +0 -1
- package/lib/npm.d.ts +0 -223
- package/lib/npm.js +0 -76
- package/lib/npm.js.map +0 -1
package/lib/widget.js
CHANGED
|
@@ -1,69 +1,19 @@
|
|
|
1
1
|
// Copyright (c) Jupyter Development Team.
|
|
2
2
|
// Distributed under the terms of the Modified BSD License.
|
|
3
|
-
import {
|
|
4
|
-
import { nullTranslator } from '@jupyterlab/translation';
|
|
5
|
-
import { Button, caretDownIcon, caretRightIcon, Collapse, InputGroup, jupyterIcon, listingsInfoIcon, refreshIcon, ToolbarButtonComponent, VDomRenderer } from '@jupyterlab/ui-components';
|
|
3
|
+
import { Button, FilterBox, infoIcon, jupyterIcon, PanelWithToolbar, ReactWidget, refreshIcon, SidePanel, ToolbarButton, ToolbarButtonComponent } from '@jupyterlab/ui-components';
|
|
6
4
|
import * as React from 'react';
|
|
7
5
|
import ReactPaginate from 'react-paginate';
|
|
8
6
|
import { ListModel } from './model';
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Icons with custom styling bound.
|
|
13
|
-
*/
|
|
14
|
-
const caretDownIconStyled = caretDownIcon.bindprops({
|
|
15
|
-
height: 'auto',
|
|
16
|
-
width: '20px'
|
|
17
|
-
});
|
|
18
|
-
const caretRightIconStyled = caretRightIcon.bindprops({
|
|
19
|
-
height: 'auto',
|
|
20
|
-
width: '20px'
|
|
21
|
-
});
|
|
22
|
-
const badgeSize = 32;
|
|
23
|
-
const badgeQuerySize = Math.floor(devicePixelRatio * badgeSize);
|
|
24
|
-
/**
|
|
25
|
-
* Search bar VDOM component.
|
|
26
|
-
*/
|
|
27
|
-
export class SearchBar extends React.Component {
|
|
28
|
-
constructor(props) {
|
|
29
|
-
super(props);
|
|
30
|
-
/**
|
|
31
|
-
* Handler for search input changes.
|
|
32
|
-
*/
|
|
33
|
-
this.handleChange = (e) => {
|
|
34
|
-
const target = e.target;
|
|
35
|
-
this.setState({
|
|
36
|
-
value: target.value
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
|
-
this.state = {
|
|
40
|
-
value: ''
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Render the list view using the virtual DOM.
|
|
45
|
-
*/
|
|
46
|
-
render() {
|
|
47
|
-
return (React.createElement("div", { className: "jp-extensionmanager-search-bar" },
|
|
48
|
-
React.createElement(InputGroup, { className: "jp-extensionmanager-search-wrapper", type: "text", placeholder: this.props.placeholder, onChange: this.handleChange, value: this.state.value, rightIcon: "ui-components:search", disabled: this.props.disabled })));
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Create a build prompt as a react element.
|
|
53
|
-
*
|
|
54
|
-
* @param props Configuration of the build prompt.
|
|
55
|
-
*/
|
|
56
|
-
function BuildPrompt(props) {
|
|
57
|
-
const translator = props.translator || nullTranslator;
|
|
58
|
-
const trans = translator.load('jupyterlab');
|
|
59
|
-
return (React.createElement("div", { className: "jp-extensionmanager-buildprompt" },
|
|
60
|
-
React.createElement("div", { className: "jp-extensionmanager-buildmessage" }, trans.__('A build is needed to include the latest changes')),
|
|
61
|
-
React.createElement(Button, { onClick: props.performBuild, minimal: true, small: true }, trans.__('Rebuild')),
|
|
62
|
-
React.createElement(Button, { onClick: props.ignoreBuild, minimal: true, small: true }, trans.__('Ignore'))));
|
|
63
|
-
}
|
|
7
|
+
const BADGE_SIZE = 32;
|
|
8
|
+
const BADGE_QUERY_SIZE = Math.floor(devicePixelRatio * BADGE_SIZE);
|
|
64
9
|
function getExtensionGitHubUser(entry) {
|
|
65
|
-
if (entry.
|
|
66
|
-
|
|
10
|
+
if (entry.homepage_url &&
|
|
11
|
+
entry.homepage_url.startsWith('https://github.com/')) {
|
|
12
|
+
return entry.homepage_url.split('/')[3];
|
|
13
|
+
}
|
|
14
|
+
else if (entry.repository_url &&
|
|
15
|
+
entry.repository_url.startsWith('https://github.com/')) {
|
|
16
|
+
return entry.repository_url.split('/')[3];
|
|
67
17
|
}
|
|
68
18
|
return null;
|
|
69
19
|
}
|
|
@@ -71,288 +21,130 @@ function getExtensionGitHubUser(entry) {
|
|
|
71
21
|
* VDOM for visualizing an extension entry.
|
|
72
22
|
*/
|
|
73
23
|
function ListEntry(props) {
|
|
74
|
-
|
|
75
|
-
const { entry, listMode, viewType } = props;
|
|
76
|
-
const translator = props.translator || nullTranslator;
|
|
77
|
-
const trans = translator.load('jupyterlab');
|
|
24
|
+
const { entry, supportInstallation, trans } = props;
|
|
78
25
|
const flagClasses = [];
|
|
79
26
|
if (entry.status && ['ok', 'warning', 'error'].indexOf(entry.status) !== -1) {
|
|
80
27
|
flagClasses.push(`jp-extensionmanager-entry-${entry.status}`);
|
|
81
28
|
}
|
|
82
|
-
|
|
83
|
-
const entryIsJupyterOrg = isJupyterOrg(entry.name);
|
|
84
|
-
if (entryIsJupyterOrg) {
|
|
85
|
-
title = trans.__('%1 (Developed by Project Jupyter)', entry.name);
|
|
86
|
-
}
|
|
29
|
+
const title = entry.name;
|
|
87
30
|
const githubUser = getExtensionGitHubUser(entry);
|
|
88
|
-
if (
|
|
89
|
-
entry.blockedExtensionsEntry &&
|
|
90
|
-
viewType === 'searchResult') {
|
|
91
|
-
return React.createElement("li", null);
|
|
92
|
-
}
|
|
93
|
-
if (listMode === 'allow' &&
|
|
94
|
-
!entry.allowedExtensionsEntry &&
|
|
95
|
-
viewType === 'searchResult') {
|
|
96
|
-
return React.createElement("li", null);
|
|
97
|
-
}
|
|
98
|
-
if (listMode === 'block' && ((_a = entry.blockedExtensionsEntry) === null || _a === void 0 ? void 0 : _a.name)) {
|
|
99
|
-
flagClasses.push(`jp-extensionmanager-entry-should-be-uninstalled`);
|
|
100
|
-
}
|
|
101
|
-
if (listMode === 'allow' && !entry.allowedExtensionsEntry) {
|
|
31
|
+
if (!entry.allowed) {
|
|
102
32
|
flagClasses.push(`jp-extensionmanager-entry-should-be-uninstalled`);
|
|
103
33
|
}
|
|
104
34
|
return (React.createElement("li", { className: `jp-extensionmanager-entry ${flagClasses.join(' ')}`, title: title, style: { display: 'flex' } },
|
|
105
|
-
React.createElement("div", { style: { marginRight: '8px' } },
|
|
106
|
-
githubUser && (React.createElement("img", { src: `https://github.com/${githubUser}.png?size=${badgeQuerySize}`, style: { width: '32px', height: '32px' } })),
|
|
107
|
-
!githubUser && (React.createElement("div", { style: { width: `${badgeSize}px`, height: `${badgeSize}px` } }))),
|
|
35
|
+
React.createElement("div", { style: { marginRight: '8px' } }, githubUser ? (React.createElement("img", { src: `https://github.com/${githubUser}.png?size=${BADGE_QUERY_SIZE}`, style: { width: '32px', height: '32px' } })) : (React.createElement("div", { style: { width: `${BADGE_SIZE}px`, height: `${BADGE_SIZE}px` } }))),
|
|
108
36
|
React.createElement("div", { className: "jp-extensionmanager-entry-description" },
|
|
109
37
|
React.createElement("div", { className: "jp-extensionmanager-entry-title" },
|
|
110
|
-
React.createElement("div", { className: "jp-extensionmanager-entry-name" }, entry.
|
|
111
|
-
entry.
|
|
112
|
-
|
|
113
|
-
viewType === 'installed' &&
|
|
114
|
-
listMode === 'allow' && (React.createElement(ToolbarButtonComponent, { icon: listingsInfoIcon, iconLabel: trans.__('%1 extension has been removed from the allowedExtensions since installation. Please uninstall immediately and contact your allowedExtensions administrator.', entry.name), onClick: () => window.open('https://jupyterlab.readthedocs.io/en/latest/user/extensions.html') })),
|
|
115
|
-
entryIsJupyterOrg && (React.createElement(jupyterIcon.react, { className: "jp-extensionmanager-is-jupyter-org", top: "1px", height: "auto", width: "1em" }))),
|
|
38
|
+
React.createElement("div", { className: "jp-extensionmanager-entry-name" }, entry.homepage_url ? (React.createElement("a", { href: entry.homepage_url, target: "_blank", rel: "noopener noreferrer" }, entry.name)) : (React.createElement("div", null, entry.name))),
|
|
39
|
+
entry.installed && !entry.allowed && (React.createElement(ToolbarButtonComponent, { icon: infoIcon, iconLabel: trans.__('%1 extension is not allowed any more. Please uninstall immediately or contact your administrator.', entry.name), onClick: () => window.open('https://jupyterlab.readthedocs.io/en/latest/user/extensions.html') })),
|
|
40
|
+
entry.approved && (React.createElement(jupyterIcon.react, { className: "jp-extensionmanager-is-approved", top: "1px", height: "auto", width: "1em", title: trans.__('This extension is approved by your security team.') }))),
|
|
116
41
|
React.createElement("div", { className: "jp-extensionmanager-entry-content" },
|
|
117
42
|
React.createElement("div", { className: "jp-extensionmanager-entry-description" }, entry.description),
|
|
118
|
-
React.createElement("div", { className: "jp-extensionmanager-entry-buttons" },
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
title,
|
|
124
|
-
body: (React.createElement("div", null, getPrebuiltUninstallInstruction(entry, trans))),
|
|
125
|
-
buttons: [
|
|
126
|
-
Dialog.okButton({
|
|
127
|
-
label: trans.__('OK'),
|
|
128
|
-
caption: trans.__('OK')
|
|
129
|
-
})
|
|
130
|
-
]
|
|
131
|
-
}).then(result => {
|
|
132
|
-
return result.button.accept;
|
|
133
|
-
}), minimal: true, small: true }, trans.__('About')))))))));
|
|
134
|
-
}
|
|
135
|
-
function getPrebuiltUninstallInstruction(entry, trans) {
|
|
136
|
-
var _a, _b;
|
|
137
|
-
if ((_a = entry.install) === null || _a === void 0 ? void 0 : _a.uninstallInstructions) {
|
|
138
|
-
return (React.createElement("div", null,
|
|
139
|
-
React.createElement("p", null, trans.__(`This is a prebuilt extension. To uninstall it, please
|
|
140
|
-
apply following instructions.`)),
|
|
141
|
-
React.createElement("p", null, trans.__((_b = entry.install) === null || _b === void 0 ? void 0 : _b.uninstallInstructions))));
|
|
142
|
-
}
|
|
143
|
-
return (React.createElement("div", null,
|
|
144
|
-
React.createElement("p", null, trans.__(`This is a prebuilt extension. To uninstall it, please
|
|
145
|
-
read the user guide on:`)),
|
|
146
|
-
React.createElement("p", null,
|
|
147
|
-
React.createElement("a", { href: "https://jupyterlab.readthedocs.io/en/latest/user/extensions.html", target: "_blank", rel: "noopener noreferrer" }, "https://jupyterlab.readthedocs.io/en/latest/user/extensions.html"))));
|
|
43
|
+
props.performAction && (React.createElement("div", { className: "jp-extensionmanager-entry-buttons" }, entry.installed ? (React.createElement(React.Fragment, null,
|
|
44
|
+
supportInstallation && (React.createElement(React.Fragment, null,
|
|
45
|
+
ListModel.entryHasUpdate(entry) && (React.createElement(Button, { onClick: () => props.performAction('install', entry), minimal: true, small: true }, trans.__('Update'))),
|
|
46
|
+
React.createElement(Button, { onClick: () => props.performAction('uninstall', entry), minimal: true, small: true }, trans.__('Uninstall')))),
|
|
47
|
+
entry.enabled ? (React.createElement(Button, { onClick: () => props.performAction('disable', entry), minimal: true, small: true }, trans.__('Disable'))) : (React.createElement(Button, { onClick: () => props.performAction('enable', entry), minimal: true, small: true }, trans.__('Enable'))))) : (supportInstallation && (React.createElement(Button, { onClick: () => props.performAction('install', entry), minimal: true, small: true }, trans.__('Install'))))))))));
|
|
148
48
|
}
|
|
149
49
|
/**
|
|
150
50
|
* List view widget for extensions
|
|
151
51
|
*/
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const trans =
|
|
155
|
-
const entryViews = [];
|
|
156
|
-
for (const entry of props.entries) {
|
|
157
|
-
entryViews.push(React.createElement(ListEntry, { entry: entry, listMode: props.listMode, viewType: props.viewType, key: entry.name, performAction: props.performAction, translator: translator }));
|
|
158
|
-
}
|
|
159
|
-
let pagination;
|
|
160
|
-
if (props.numPages > 1) {
|
|
161
|
-
pagination = (React.createElement("div", { className: "jp-extensionmanager-pagination" },
|
|
162
|
-
React.createElement(ReactPaginate, { previousLabel: '<', nextLabel: '>', breakLabel: React.createElement("a", { href: "" }, "..."), breakClassName: 'break-me', pageCount: props.numPages, marginPagesDisplayed: 2, pageRangeDisplayed: 5, onPageChange: (data) => props.onPage(data.selected), containerClassName: 'pagination', activeClassName: 'active' })));
|
|
163
|
-
}
|
|
164
|
-
const listview = (React.createElement("ul", { className: "jp-extensionmanager-listview" }, entryViews));
|
|
52
|
+
function ListView(props) {
|
|
53
|
+
var _a;
|
|
54
|
+
const { trans } = props;
|
|
165
55
|
return (React.createElement("div", { className: "jp-extensionmanager-listview-wrapper" },
|
|
166
|
-
|
|
167
|
-
pagination
|
|
56
|
+
props.entries.length > 0 ? (React.createElement("ul", { className: "jp-extensionmanager-listview" }, props.entries.map(entry => (React.createElement(ListEntry, { key: entry.name, entry: entry, performAction: props.performAction, supportInstallation: props.supportInstallation, trans: trans }))))) : (React.createElement("div", { key: "message", className: "jp-extensionmanager-listview-message" }, trans.__('No entries'))),
|
|
57
|
+
props.numPages > 1 && (React.createElement("div", { className: "jp-extensionmanager-pagination" },
|
|
58
|
+
React.createElement(ReactPaginate, { previousLabel: '<', nextLabel: '>', breakLabel: React.createElement("a", { href: "" }, "..."), breakClassName: 'break-me', initialPage: ((_a = props.initialPage) !== null && _a !== void 0 ? _a : 1) - 1, pageCount: props.numPages, marginPagesDisplayed: 2, pageRangeDisplayed: 5, onPageChange: (data) => props.onPage(data.selected + 1), containerClassName: 'pagination', activeClassName: 'active' })))));
|
|
168
59
|
}
|
|
169
60
|
function ErrorMessage(props) {
|
|
170
|
-
return
|
|
61
|
+
return React.createElement("div", { className: "jp-extensionmanager-error" }, props.children);
|
|
171
62
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
this.
|
|
179
|
-
|
|
180
|
-
};
|
|
63
|
+
class Header extends ReactWidget {
|
|
64
|
+
constructor(model, trans, searchInputRef) {
|
|
65
|
+
super();
|
|
66
|
+
this.model = model;
|
|
67
|
+
this.trans = trans;
|
|
68
|
+
this.searchInputRef = searchInputRef;
|
|
69
|
+
model.stateChanged.connect(this.update, this);
|
|
70
|
+
this.addClass('jp-extensionmanager-header');
|
|
181
71
|
}
|
|
182
|
-
/**
|
|
183
|
-
* Render the collapsible section using the virtual DOM.
|
|
184
|
-
*/
|
|
185
72
|
render() {
|
|
186
|
-
let icon = this.state.isOpen ? caretDownIconStyled : caretRightIconStyled;
|
|
187
|
-
let isOpen = this.state.isOpen;
|
|
188
|
-
let className = 'jp-extensionmanager-headerText';
|
|
189
|
-
if (this.props.disabled) {
|
|
190
|
-
icon = caretRightIconStyled;
|
|
191
|
-
isOpen = false;
|
|
192
|
-
className = 'jp-extensionmanager-headerTextDisabled';
|
|
193
|
-
}
|
|
194
73
|
return (React.createElement(React.Fragment, null,
|
|
195
|
-
React.createElement("div", { className: "jp-
|
|
196
|
-
React.createElement(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
React.createElement(
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
handleCollapse() {
|
|
207
|
-
this.setState({
|
|
208
|
-
isOpen: !this.state.isOpen
|
|
209
|
-
}, () => {
|
|
210
|
-
if (this.props.onCollapse) {
|
|
211
|
-
this.props.onCollapse(this.state.isOpen);
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
UNSAFE_componentWillReceiveProps(nextProps) {
|
|
216
|
-
if (nextProps.forceOpen) {
|
|
217
|
-
this.setState({
|
|
218
|
-
isOpen: true
|
|
219
|
-
});
|
|
220
|
-
}
|
|
74
|
+
React.createElement("div", { className: "jp-extensionmanager-title" },
|
|
75
|
+
React.createElement("span", null, this.trans.__('%1 Manager', this.model.name)),
|
|
76
|
+
this.model.installPath && (React.createElement(infoIcon.react, { className: "jp-extensionmanager-path", tag: "span", title: this.trans.__('Extension installation path: %1', this.model.installPath) }))),
|
|
77
|
+
React.createElement(FilterBox, { placeholder: this.trans.__('Search'), disabled: !this.model.isDisclaimed, updateFilter: (fn, query) => {
|
|
78
|
+
this.model.query = query !== null && query !== void 0 ? query : '';
|
|
79
|
+
}, useFuzzyFilter: false, inputRef: this.searchInputRef }),
|
|
80
|
+
React.createElement("div", { className: `jp-extensionmanager-pending ${this.model.hasPendingActions() ? 'jp-mod-hasPending' : ''}` }),
|
|
81
|
+
this.model.actionError && (React.createElement(ErrorMessage, null,
|
|
82
|
+
React.createElement("p", null, this.trans.__('Error when performing an action.')),
|
|
83
|
+
React.createElement("p", null, this.trans.__('Reason given:')),
|
|
84
|
+
React.createElement("pre", null, this.model.actionError)))));
|
|
221
85
|
}
|
|
222
86
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
this.
|
|
230
|
-
this._trans = this.translator.load('jupyterlab');
|
|
231
|
-
this._settings = settings;
|
|
232
|
-
this._forceOpen = false;
|
|
233
|
-
this.addClass('jp-extensionmanager-view');
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* The search input node.
|
|
237
|
-
*/
|
|
238
|
-
get inputNode() {
|
|
239
|
-
return this.node.querySelector('.jp-extensionmanager-search-wrapper input');
|
|
87
|
+
class Warning extends ReactWidget {
|
|
88
|
+
constructor(model, trans) {
|
|
89
|
+
super();
|
|
90
|
+
this.model = model;
|
|
91
|
+
this.trans = trans;
|
|
92
|
+
this.addClass('jp-extensionmanager-disclaimer');
|
|
93
|
+
model.stateChanged.connect(this.update, this);
|
|
240
94
|
}
|
|
241
|
-
/**
|
|
242
|
-
* Render the extension view using the virtual DOM.
|
|
243
|
-
*/
|
|
244
95
|
render() {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return [React.createElement("div", { key: "empty" })];
|
|
249
|
-
}
|
|
250
|
-
if (model.listMode === 'invalid') {
|
|
251
|
-
return [
|
|
252
|
-
React.createElement("div", { style: { padding: 8 }, key: "invalid" },
|
|
253
|
-
React.createElement("div", null, this._trans
|
|
254
|
-
.__(`The extension manager is disabled. Please contact your system
|
|
255
|
-
administrator to verify the listings configuration.`)),
|
|
256
|
-
React.createElement("div", null,
|
|
257
|
-
React.createElement("a", { href: "https://jupyterlab.readthedocs.io/en/latest/user/extensions.html", target: "_blank", rel: "noopener noreferrer" }, this._trans.__('Read more in the JupyterLab documentation.'))))
|
|
258
|
-
];
|
|
259
|
-
}
|
|
260
|
-
const pages = Math.ceil(model.totalEntries / model.pagination);
|
|
261
|
-
const elements = [
|
|
262
|
-
React.createElement(SearchBar, { key: "searchbar", placeholder: this._trans.__('SEARCH'), disabled: !ListModel.isDisclaimed(), settings: this._settings })
|
|
263
|
-
];
|
|
264
|
-
if (model.promptBuild) {
|
|
265
|
-
elements.push(React.createElement(BuildPrompt, { key: "promt", translator: this.translator, performBuild: () => {
|
|
266
|
-
model.performBuild();
|
|
267
|
-
}, ignoreBuild: () => {
|
|
268
|
-
model.ignoreBuildRecommendation();
|
|
269
|
-
} }));
|
|
270
|
-
}
|
|
271
|
-
// Indicator element for pending actions:
|
|
272
|
-
elements.push(React.createElement("div", { key: "pending", className: `jp-extensionmanager-pending ${model.hasPendingActions() ? 'jp-mod-hasPending' : ''}` }));
|
|
273
|
-
const content = [];
|
|
274
|
-
content.push(React.createElement(CollapsibleSection, { key: "warning-section", isOpen: !ListModel.isDisclaimed(), disabled: false, header: this._trans.__('Warning') },
|
|
275
|
-
React.createElement("div", { className: "jp-extensionmanager-disclaimer" },
|
|
276
|
-
React.createElement("div", null, this._trans
|
|
277
|
-
.__(`The JupyterLab development team is excited to have a robust
|
|
96
|
+
return (React.createElement(React.Fragment, null,
|
|
97
|
+
React.createElement("p", null, this.trans
|
|
98
|
+
.__(`The JupyterLab development team is excited to have a robust
|
|
278
99
|
third-party extension community. However, we do not review
|
|
279
100
|
third-party extensions, and some extensions may introduce security
|
|
280
101
|
risks or contain malicious code that runs on your machine.`)),
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
.__(`Error communicating with server extension. Consult the documentation
|
|
300
|
-
for how to ensure that it is enabled.`)),
|
|
301
|
-
React.createElement("p", null, this._trans.__('Reason given:')),
|
|
302
|
-
React.createElement("pre", null, model.serverConnectionError)));
|
|
303
|
-
}
|
|
304
|
-
else if (model.serverRequirementsError !== null) {
|
|
305
|
-
content.push(React.createElement(ErrorMessage, { key: "server-requirements-error" },
|
|
306
|
-
React.createElement("p", null, this._trans.__('The server has some missing requirements for installing extensions.')),
|
|
307
|
-
React.createElement("p", null, this._trans.__('Details:')),
|
|
308
|
-
React.createElement("pre", null, model.serverRequirementsError)));
|
|
309
|
-
}
|
|
310
|
-
else {
|
|
311
|
-
// List installed and discovery sections
|
|
312
|
-
const installedContent = [];
|
|
313
|
-
if (model.installedError !== null) {
|
|
314
|
-
installedContent.push(React.createElement(ErrorMessage, { key: "install-error" }, `Error querying installed extensions${model.installedError ? `: ${model.installedError}` : '.'}`));
|
|
315
|
-
}
|
|
316
|
-
else {
|
|
317
|
-
const query = new RegExp((_b = (_a = model.query) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== null && _b !== void 0 ? _b : '');
|
|
318
|
-
installedContent.push(React.createElement(ListView, { key: "installed-items", listMode: model.listMode, viewType: 'installed', entries: model.installed.filter(pkg => !model.query || query.test(pkg.name)), numPages: 1, translator: this.translator, onPage: value => {
|
|
319
|
-
/* no-op */
|
|
320
|
-
}, performAction: this.onAction.bind(this) }));
|
|
321
|
-
}
|
|
322
|
-
content.push(React.createElement(CollapsibleSection, { key: "installed-section", isOpen: ListModel.isDisclaimed(), forceOpen: this._forceOpen, disabled: !ListModel.isDisclaimed(), header: this._trans.__('Installed'), headerElements: React.createElement(ToolbarButtonComponent, { key: "refresh-button", icon: refreshIcon, onClick: () => {
|
|
323
|
-
model.refreshInstalled();
|
|
324
|
-
}, tooltip: this._trans.__('Refresh extension list') }) }, installedContent));
|
|
325
|
-
const searchContent = [];
|
|
326
|
-
if (model.searchError !== null) {
|
|
327
|
-
searchContent.push(React.createElement(ErrorMessage, { key: "search-error" }, `Error searching for extensions${model.searchError ? `: ${model.searchError}` : '.'}`));
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
searchContent.push(React.createElement(ListView, { key: "search-items", listMode: model.listMode, viewType: 'searchResult',
|
|
331
|
-
// Filter out installed extensions:
|
|
332
|
-
entries: model.searchResult.filter(entry => model.installed.indexOf(entry) === -1), numPages: pages, onPage: value => {
|
|
333
|
-
this.onPage(value);
|
|
334
|
-
}, performAction: this.onAction.bind(this), translator: this.translator }));
|
|
335
|
-
}
|
|
336
|
-
content.push(React.createElement(CollapsibleSection, { key: "search-section", isOpen: ListModel.isDisclaimed(), forceOpen: this._forceOpen, disabled: !ListModel.isDisclaimed(), header: model.query
|
|
337
|
-
? this._trans.__('Search Results')
|
|
338
|
-
: this._trans.__('Discover'), onCollapse: (isOpen) => {
|
|
339
|
-
if (isOpen && model.query === null) {
|
|
340
|
-
model.query = '';
|
|
341
|
-
}
|
|
342
|
-
} }, searchContent));
|
|
343
|
-
}
|
|
344
|
-
elements.push(React.createElement("div", { key: "content", className: "jp-extensionmanager-content" }, content));
|
|
345
|
-
// Reset the force open for future usage.
|
|
346
|
-
this._forceOpen = false;
|
|
347
|
-
return elements;
|
|
102
|
+
this.model.isDisclaimed ? (React.createElement(Button, { className: "jp-extensionmanager-disclaimer-disable", onClick: (e) => {
|
|
103
|
+
this.model.isDisclaimed = false;
|
|
104
|
+
} }, this.trans.__('Disable'))) : (React.createElement(Button, { className: "jp-extensionmanager-disclaimer-enable", onClick: (e) => {
|
|
105
|
+
this.model.isDisclaimed = true;
|
|
106
|
+
} }, this.trans.__('Enable')))));
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
class InstalledList extends ReactWidget {
|
|
110
|
+
constructor(model, trans) {
|
|
111
|
+
super();
|
|
112
|
+
this.model = model;
|
|
113
|
+
this.trans = trans;
|
|
114
|
+
model.stateChanged.connect(this.update, this);
|
|
115
|
+
}
|
|
116
|
+
render() {
|
|
117
|
+
return (React.createElement(React.Fragment, null, this.model.installedError !== null ? (React.createElement(ErrorMessage, null, `Error querying installed extensions${this.model.installedError ? `: ${this.model.installedError}` : '.'}`)) : this.model.isLoadingInstalledExtensions ? (React.createElement("div", { className: "jp-extensionmanager-loader" }, this.trans.__('Updating extensions list…'))) : (React.createElement(ListView, { entries: this.model.installed.filter(pkg => new RegExp(this.model.query.toLowerCase()).test(pkg.name)), numPages: 1, trans: this.trans, onPage: value => {
|
|
118
|
+
/* no-op */
|
|
119
|
+
}, performAction: this.model.isDisclaimed ? this.onAction.bind(this) : null, supportInstallation: this.model.canInstall && this.model.isDisclaimed }))));
|
|
348
120
|
}
|
|
349
121
|
/**
|
|
350
|
-
* Callback handler for the user
|
|
122
|
+
* Callback handler for when the user wants to perform an action on an extension.
|
|
351
123
|
*
|
|
352
|
-
* @param
|
|
124
|
+
* @param action The action to perform.
|
|
125
|
+
* @param entry The entry to perform the action on.
|
|
353
126
|
*/
|
|
354
|
-
|
|
355
|
-
|
|
127
|
+
onAction(action, entry) {
|
|
128
|
+
switch (action) {
|
|
129
|
+
case 'install':
|
|
130
|
+
return this.model.install(entry);
|
|
131
|
+
case 'uninstall':
|
|
132
|
+
return this.model.uninstall(entry);
|
|
133
|
+
case 'enable':
|
|
134
|
+
return this.model.enable(entry);
|
|
135
|
+
case 'disable':
|
|
136
|
+
return this.model.disable(entry);
|
|
137
|
+
default:
|
|
138
|
+
throw new Error(`Invalid action: ${action}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
class SearchResult extends ReactWidget {
|
|
143
|
+
constructor(model, trans) {
|
|
144
|
+
super();
|
|
145
|
+
this.model = model;
|
|
146
|
+
this.trans = trans;
|
|
147
|
+
model.stateChanged.connect(this.update, this);
|
|
356
148
|
}
|
|
357
149
|
/**
|
|
358
150
|
* Callback handler for the user changes the page of the search result pagination.
|
|
@@ -382,6 +174,74 @@ risks or contain malicious code that runs on your machine.`)),
|
|
|
382
174
|
throw new Error(`Invalid action: ${action}`);
|
|
383
175
|
}
|
|
384
176
|
}
|
|
177
|
+
render() {
|
|
178
|
+
return (React.createElement(React.Fragment, null, this.model.searchError !== null ? (React.createElement(ErrorMessage, null, `Error searching for extensions${this.model.searchError ? `: ${this.model.searchError}` : '.'}`)) : this.model.isSearching ? (React.createElement("div", { className: "jp-extensionmanager-loader" }, this.trans.__('Updating extensions list…'))) : (React.createElement(ListView, { entries: this.model.searchResult, initialPage: this.model.page, numPages: this.model.lastPage, onPage: value => {
|
|
179
|
+
this.onPage(value);
|
|
180
|
+
}, performAction: this.model.isDisclaimed ? this.onAction.bind(this) : null, supportInstallation: this.model.canInstall && this.model.isDisclaimed, trans: this.trans }))));
|
|
181
|
+
}
|
|
182
|
+
update() {
|
|
183
|
+
this.title.label = this.model.query
|
|
184
|
+
? this.trans.__('Search Results')
|
|
185
|
+
: this.trans.__('Discover');
|
|
186
|
+
super.update();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export class ExtensionsPanel extends SidePanel {
|
|
190
|
+
constructor(options) {
|
|
191
|
+
const { model, translator } = options;
|
|
192
|
+
super({ translator });
|
|
193
|
+
this._wasInitialized = false;
|
|
194
|
+
this._wasDisclaimed = true;
|
|
195
|
+
this.model = model;
|
|
196
|
+
this._searchInputRef = React.createRef();
|
|
197
|
+
this.addClass('jp-extensionmanager-view');
|
|
198
|
+
this.trans = translator.load('jupyterlab');
|
|
199
|
+
this.header.addWidget(new Header(model, this.trans, this._searchInputRef));
|
|
200
|
+
const warning = new Warning(model, this.trans);
|
|
201
|
+
warning.title.label = this.trans.__('Warning');
|
|
202
|
+
this.addWidget(warning);
|
|
203
|
+
const installed = new PanelWithToolbar();
|
|
204
|
+
installed.addClass('jp-extensionmanager-installedlist');
|
|
205
|
+
installed.title.label = this.trans.__('Installed');
|
|
206
|
+
installed.toolbar.addItem('refresh', new ToolbarButton({
|
|
207
|
+
icon: refreshIcon,
|
|
208
|
+
onClick: () => {
|
|
209
|
+
model.refreshInstalled(true).catch(reason => {
|
|
210
|
+
console.error(`Failed to refresh the installed extensions list:\n${reason}`);
|
|
211
|
+
});
|
|
212
|
+
},
|
|
213
|
+
tooltip: this.trans.__('Refresh extensions list')
|
|
214
|
+
}));
|
|
215
|
+
installed.addWidget(new InstalledList(model, this.trans));
|
|
216
|
+
this.addWidget(installed);
|
|
217
|
+
if (this.model.canInstall) {
|
|
218
|
+
const searchResults = new SearchResult(model, this.trans);
|
|
219
|
+
searchResults.addClass('jp-extensionmanager-searchresults');
|
|
220
|
+
this.addWidget(searchResults);
|
|
221
|
+
}
|
|
222
|
+
this._wasDisclaimed = this.model.isDisclaimed;
|
|
223
|
+
if (this.model.isDisclaimed) {
|
|
224
|
+
this.content.collapse(0);
|
|
225
|
+
this.content.layout.setRelativeSizes([0, 1, 1]);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
// If warning is not disclaimed expand only the warning panel
|
|
229
|
+
this.content.expand(0);
|
|
230
|
+
this.content.collapse(1);
|
|
231
|
+
this.content.collapse(2);
|
|
232
|
+
}
|
|
233
|
+
this.model.stateChanged.connect(this._onDisclaimedChanged, this);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Dispose of the widget and its descendant widgets.
|
|
237
|
+
*/
|
|
238
|
+
dispose() {
|
|
239
|
+
if (this.isDisposed) {
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
this.model.stateChanged.disconnect(this._onDisclaimedChanged, this);
|
|
243
|
+
super.dispose();
|
|
244
|
+
}
|
|
385
245
|
/**
|
|
386
246
|
* Handle the DOM events for the extension manager search bar.
|
|
387
247
|
*
|
|
@@ -394,9 +254,6 @@ risks or contain malicious code that runs on your machine.`)),
|
|
|
394
254
|
*/
|
|
395
255
|
handleEvent(event) {
|
|
396
256
|
switch (event.type) {
|
|
397
|
-
case 'input':
|
|
398
|
-
this.onSearch(this.inputNode.value);
|
|
399
|
-
break;
|
|
400
257
|
case 'focus':
|
|
401
258
|
case 'blur':
|
|
402
259
|
this._toggleFocused();
|
|
@@ -409,15 +266,23 @@ risks or contain malicious code that runs on your machine.`)),
|
|
|
409
266
|
* A message handler invoked on a `'before-attach'` message.
|
|
410
267
|
*/
|
|
411
268
|
onBeforeAttach(msg) {
|
|
412
|
-
this.node.addEventListener('input', this);
|
|
413
269
|
this.node.addEventListener('focus', this, true);
|
|
414
270
|
this.node.addEventListener('blur', this, true);
|
|
271
|
+
super.onBeforeAttach(msg);
|
|
272
|
+
}
|
|
273
|
+
onBeforeShow(msg) {
|
|
274
|
+
if (!this._wasInitialized) {
|
|
275
|
+
this._wasInitialized = true;
|
|
276
|
+
this.model.refreshInstalled().catch(reason => {
|
|
277
|
+
console.log(`Failed to refresh installed extension list:\n${reason}`);
|
|
278
|
+
});
|
|
279
|
+
}
|
|
415
280
|
}
|
|
416
281
|
/**
|
|
417
282
|
* A message handler invoked on an `'after-detach'` message.
|
|
418
283
|
*/
|
|
419
284
|
onAfterDetach(msg) {
|
|
420
|
-
|
|
285
|
+
super.onAfterDetach(msg);
|
|
421
286
|
this.node.removeEventListener('focus', this, true);
|
|
422
287
|
this.node.removeEventListener('blur', this, true);
|
|
423
288
|
}
|
|
@@ -426,18 +291,27 @@ risks or contain malicious code that runs on your machine.`)),
|
|
|
426
291
|
*/
|
|
427
292
|
onActivateRequest(msg) {
|
|
428
293
|
if (this.isAttached) {
|
|
429
|
-
const input = this.
|
|
294
|
+
const input = this._searchInputRef.current;
|
|
430
295
|
if (input) {
|
|
431
296
|
input.focus();
|
|
432
297
|
input.select();
|
|
433
298
|
}
|
|
434
299
|
}
|
|
300
|
+
super.onActivateRequest(msg);
|
|
301
|
+
}
|
|
302
|
+
_onDisclaimedChanged() {
|
|
303
|
+
if (!this._wasDisclaimed && this.model.isDisclaimed) {
|
|
304
|
+
this.content.collapse(0);
|
|
305
|
+
this.content.expand(1);
|
|
306
|
+
this.content.expand(2);
|
|
307
|
+
this.content.layout.setRelativeSizes([0, 1, 1]);
|
|
308
|
+
}
|
|
435
309
|
}
|
|
436
310
|
/**
|
|
437
311
|
* Toggle the focused modifier based on the input node focus state.
|
|
438
312
|
*/
|
|
439
313
|
_toggleFocused() {
|
|
440
|
-
const focused = document.activeElement === this.
|
|
314
|
+
const focused = document.activeElement === this._searchInputRef.current;
|
|
441
315
|
this.toggleClass('lm-mod-focused', focused);
|
|
442
316
|
}
|
|
443
317
|
}
|