@jupyterlab/pluginmanager 4.1.0-alpha.2
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/dialogs.d.ts +13 -0
- package/lib/dialogs.js +14 -0
- package/lib/dialogs.js.map +1 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +12 -0
- package/lib/index.js.map +1 -0
- package/lib/model.d.ts +180 -0
- package/lib/model.js +322 -0
- package/lib/model.js.map +1 -0
- package/lib/tokens.d.ts +14 -0
- package/lib/tokens.js +8 -0
- package/lib/tokens.js.map +1 -0
- package/lib/widget.d.ts +23 -0
- package/lib/widget.js +184 -0
- package/lib/widget.js.map +1 -0
- package/package.json +64 -0
- package/src/dialogs.tsx +47 -0
- package/src/index.ts +11 -0
- package/src/model.ts +478 -0
- package/src/tokens.ts +22 -0
- package/src/widget.tsx +314 -0
- package/style/base.css +31 -0
- package/style/index.css +11 -0
- package/style/index.js +12 -0
package/src/widget.tsx
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { VDomRenderer } from '@jupyterlab/apputils';
|
|
7
|
+
import { ITranslator, TranslationBundle } from '@jupyterlab/translation';
|
|
8
|
+
import { FilterBox, lockIcon, Table } from '@jupyterlab/ui-components';
|
|
9
|
+
import { Panel } from '@lumino/widgets';
|
|
10
|
+
import * as React from 'react';
|
|
11
|
+
import { Action, IEntry, PluginListModel } from './model';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* A namespace for plugins panel.
|
|
15
|
+
*/
|
|
16
|
+
export namespace Plugins {
|
|
17
|
+
/**
|
|
18
|
+
* The initialization options for a plugins panel.
|
|
19
|
+
*/
|
|
20
|
+
export interface IOptions {
|
|
21
|
+
model: PluginListModel;
|
|
22
|
+
translator: ITranslator;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Panel with a table of available plugins allowing to enable/disable each.
|
|
28
|
+
*/
|
|
29
|
+
export class Plugins extends Panel {
|
|
30
|
+
constructor(options: Plugins.IOptions) {
|
|
31
|
+
const { model, translator } = options;
|
|
32
|
+
super();
|
|
33
|
+
this.model = model;
|
|
34
|
+
this.addClass('jp-pluginmanager');
|
|
35
|
+
|
|
36
|
+
this.trans = translator.load('jupyterlab');
|
|
37
|
+
|
|
38
|
+
this.addWidget(new Disclaimer(model, this.trans));
|
|
39
|
+
|
|
40
|
+
const header = new Header(model, this.trans);
|
|
41
|
+
this.addWidget(header);
|
|
42
|
+
|
|
43
|
+
const availableList = new AvailableList(model, this.trans);
|
|
44
|
+
this.addWidget(availableList);
|
|
45
|
+
}
|
|
46
|
+
readonly model: PluginListModel;
|
|
47
|
+
protected trans: TranslationBundle;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
class AvailableList extends VDomRenderer<PluginListModel> {
|
|
51
|
+
constructor(
|
|
52
|
+
model: PluginListModel,
|
|
53
|
+
protected trans: TranslationBundle
|
|
54
|
+
) {
|
|
55
|
+
super(model);
|
|
56
|
+
this.addClass('jp-pluginmanager-AvailableList');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
render(): JSX.Element {
|
|
60
|
+
return (
|
|
61
|
+
<>
|
|
62
|
+
{this.model.statusError !== null ? (
|
|
63
|
+
<ErrorMessage>
|
|
64
|
+
{this.trans.__(
|
|
65
|
+
'Error querying installed extensions%1',
|
|
66
|
+
this.model.statusError ? `: ${this.model.statusError}` : '.'
|
|
67
|
+
)}
|
|
68
|
+
</ErrorMessage>
|
|
69
|
+
) : this.model.isLoading ? (
|
|
70
|
+
<div className="jp-pluginmanager-loader">
|
|
71
|
+
{this.trans.__('Updating plugin list…')}
|
|
72
|
+
</div>
|
|
73
|
+
) : (
|
|
74
|
+
<Table<IEntry>
|
|
75
|
+
blankIndicator={() => {
|
|
76
|
+
return <div>{this.trans.__('No entries')}</div>;
|
|
77
|
+
}}
|
|
78
|
+
sortKey={'plugin-id'}
|
|
79
|
+
rows={this.model.available
|
|
80
|
+
.filter(pkg => {
|
|
81
|
+
const pattern = new RegExp(this.model.query, 'i');
|
|
82
|
+
return (
|
|
83
|
+
pattern.test(pkg.id) ||
|
|
84
|
+
pattern.test(pkg.extension) ||
|
|
85
|
+
(pkg.tokenLabel && pattern.test(pkg.tokenLabel))
|
|
86
|
+
);
|
|
87
|
+
})
|
|
88
|
+
.map(data => {
|
|
89
|
+
return {
|
|
90
|
+
data: data,
|
|
91
|
+
key: data.id
|
|
92
|
+
};
|
|
93
|
+
})}
|
|
94
|
+
columns={[
|
|
95
|
+
{
|
|
96
|
+
id: 'plugin-id',
|
|
97
|
+
label: this.trans.__('Plugin'),
|
|
98
|
+
renderCell: (row: IEntry) => (
|
|
99
|
+
<>
|
|
100
|
+
<code>{row.id}</code>
|
|
101
|
+
<br />
|
|
102
|
+
{row.description}
|
|
103
|
+
</>
|
|
104
|
+
),
|
|
105
|
+
sort: (a: IEntry, b: IEntry) => a.id.localeCompare(b.id)
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 'description',
|
|
109
|
+
label: this.trans.__('Description'),
|
|
110
|
+
renderCell: (row: IEntry) => <>{row.description}</>,
|
|
111
|
+
sort: (a: IEntry, b: IEntry) =>
|
|
112
|
+
a.description && b.description
|
|
113
|
+
? a.description.localeCompare(b.description)
|
|
114
|
+
: undefined,
|
|
115
|
+
isHidden: true
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
id: 'autostart',
|
|
119
|
+
label: this.trans.__('Autostart?'),
|
|
120
|
+
renderCell: (row: IEntry) => {
|
|
121
|
+
switch (row.autoStart) {
|
|
122
|
+
case 'defer':
|
|
123
|
+
return this.trans.__('Defer');
|
|
124
|
+
case true:
|
|
125
|
+
return this.trans.__('Yes');
|
|
126
|
+
case false:
|
|
127
|
+
case undefined: // The default is `false`.
|
|
128
|
+
return this.trans.__('No');
|
|
129
|
+
default:
|
|
130
|
+
const leftover: never = row.autoStart;
|
|
131
|
+
throw new Error(`Unknown value: ${leftover}`);
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
sort: (a: IEntry, b: IEntry) =>
|
|
135
|
+
a.autoStart === b.autoStart ? 0 : a.autoStart ? -1 : 1
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 'requires',
|
|
139
|
+
label: this.trans.__('Depends on'),
|
|
140
|
+
renderCell: (row: IEntry) => (
|
|
141
|
+
<>{row.requires.map(v => v.name).join('\n')}</>
|
|
142
|
+
),
|
|
143
|
+
sort: (a: IEntry, b: IEntry) =>
|
|
144
|
+
(a.requires || []).length - (b.requires || []).length,
|
|
145
|
+
isHidden: true
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: 'extension',
|
|
149
|
+
label: this.trans.__('Extension'),
|
|
150
|
+
renderCell: (row: IEntry) => <>{row.extension}</>,
|
|
151
|
+
sort: (a: IEntry, b: IEntry) =>
|
|
152
|
+
a.extension.localeCompare(b.extension)
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'provides',
|
|
156
|
+
label: this.trans.__('Provides'),
|
|
157
|
+
renderCell: (row: IEntry) => (
|
|
158
|
+
<>
|
|
159
|
+
{row.provides ? (
|
|
160
|
+
<code title={row.provides.name}>{row.tokenLabel}</code>
|
|
161
|
+
) : (
|
|
162
|
+
'-'
|
|
163
|
+
)}
|
|
164
|
+
</>
|
|
165
|
+
),
|
|
166
|
+
sort: (a: IEntry, b: IEntry) =>
|
|
167
|
+
(a.tokenLabel || '').localeCompare(b.tokenLabel || '')
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
id: 'enabled',
|
|
171
|
+
label: this.trans.__('Enabled'),
|
|
172
|
+
renderCell: (row: IEntry) => (
|
|
173
|
+
<>
|
|
174
|
+
<input
|
|
175
|
+
type="checkbox"
|
|
176
|
+
checked={row.enabled}
|
|
177
|
+
disabled={row.locked || !this.model.isDisclaimed}
|
|
178
|
+
title={
|
|
179
|
+
row.locked || !this.model.isDisclaimed
|
|
180
|
+
? row.locked
|
|
181
|
+
? this.trans.__('This plugin is locked.')
|
|
182
|
+
: this.trans.__(
|
|
183
|
+
'To enable/disable, please acknowledge the disclaimer.'
|
|
184
|
+
)
|
|
185
|
+
: row.enabled
|
|
186
|
+
? this.trans.__('Disable %1 plugin', row.id)
|
|
187
|
+
: this.trans.__('Enable %1 plugin', row.id)
|
|
188
|
+
}
|
|
189
|
+
onChange={(
|
|
190
|
+
event: React.ChangeEvent<HTMLInputElement>
|
|
191
|
+
) => {
|
|
192
|
+
if (!this.model.isDisclaimed) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
if (event.target.checked) {
|
|
196
|
+
void this.onAction('enable', row);
|
|
197
|
+
} else {
|
|
198
|
+
void this.onAction('disable', row);
|
|
199
|
+
}
|
|
200
|
+
}}
|
|
201
|
+
/>
|
|
202
|
+
{row.locked ? (
|
|
203
|
+
<lockIcon.react
|
|
204
|
+
tag="span"
|
|
205
|
+
title={this.trans.__(
|
|
206
|
+
'This plugin was locked by system administrator or is a critical dependency and cannot be enabled/disabled.'
|
|
207
|
+
)}
|
|
208
|
+
/>
|
|
209
|
+
) : (
|
|
210
|
+
''
|
|
211
|
+
)}
|
|
212
|
+
</>
|
|
213
|
+
),
|
|
214
|
+
sort: (a: IEntry, b: IEntry) => +a.enabled - +b.enabled
|
|
215
|
+
}
|
|
216
|
+
]}
|
|
217
|
+
/>
|
|
218
|
+
)}
|
|
219
|
+
</>
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Callback handler for when the user wants to perform an action on an extension.
|
|
225
|
+
*
|
|
226
|
+
* @param action The action to perform.
|
|
227
|
+
* @param entry The entry to perform the action on.
|
|
228
|
+
*/
|
|
229
|
+
onAction(action: Action, entry: IEntry): Promise<void> {
|
|
230
|
+
switch (action) {
|
|
231
|
+
case 'enable':
|
|
232
|
+
return this.model.enable(entry);
|
|
233
|
+
case 'disable':
|
|
234
|
+
return this.model.disable(entry);
|
|
235
|
+
default:
|
|
236
|
+
throw new Error(`Invalid action: ${action}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
class Disclaimer extends VDomRenderer<PluginListModel> {
|
|
242
|
+
constructor(
|
|
243
|
+
model: PluginListModel,
|
|
244
|
+
protected trans: TranslationBundle
|
|
245
|
+
) {
|
|
246
|
+
super(model);
|
|
247
|
+
this.addClass('jp-pluginmanager-Disclaimer');
|
|
248
|
+
}
|
|
249
|
+
render(): JSX.Element {
|
|
250
|
+
return (
|
|
251
|
+
<div>
|
|
252
|
+
<div>
|
|
253
|
+
{this.trans.__(
|
|
254
|
+
'Customise your experience/improve performance by disabling plugins you do not need. To disable or uninstall an entire extension use the Extension Manager instead. Changes will apply after reloading JupyterLab.'
|
|
255
|
+
)}
|
|
256
|
+
</div>
|
|
257
|
+
<label>
|
|
258
|
+
<input
|
|
259
|
+
type="checkbox"
|
|
260
|
+
className="jp-mod-styled jp-pluginmanager-Disclaimer-checkbox"
|
|
261
|
+
defaultChecked={this.model.isDisclaimed}
|
|
262
|
+
onChange={event => {
|
|
263
|
+
this.model.isDisclaimed = event.target.checked;
|
|
264
|
+
}}
|
|
265
|
+
/>
|
|
266
|
+
{this.trans.__(
|
|
267
|
+
'I understand that disabling core application plugins may render features and parts of the user interface unavailable and recovery using `jupyter labextension enable <plugin-name>` command may be required'
|
|
268
|
+
)}
|
|
269
|
+
</label>
|
|
270
|
+
</div>
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
class Header extends VDomRenderer<PluginListModel> {
|
|
276
|
+
constructor(
|
|
277
|
+
model: PluginListModel,
|
|
278
|
+
protected trans: TranslationBundle
|
|
279
|
+
) {
|
|
280
|
+
super(model);
|
|
281
|
+
this.addClass('jp-pluginmanager-Header');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
render(): JSX.Element {
|
|
285
|
+
return (
|
|
286
|
+
<>
|
|
287
|
+
<FilterBox
|
|
288
|
+
placeholder={this.trans.__('Filter')}
|
|
289
|
+
updateFilter={(fn, query) => {
|
|
290
|
+
this.model.query = query ?? '';
|
|
291
|
+
}}
|
|
292
|
+
initialQuery={this.model.query}
|
|
293
|
+
useFuzzyFilter={false}
|
|
294
|
+
/>
|
|
295
|
+
<div
|
|
296
|
+
className={`jp-pluginmanager-pending ${
|
|
297
|
+
this.model.hasPendingActions() ? 'jp-mod-hasPending' : ''
|
|
298
|
+
}`}
|
|
299
|
+
/>
|
|
300
|
+
{this.model.actionError && (
|
|
301
|
+
<ErrorMessage>
|
|
302
|
+
<p>{this.trans.__('Error when performing an action.')}</p>
|
|
303
|
+
<p>{this.trans.__('Reason given:')}</p>
|
|
304
|
+
<pre>{this.model.actionError}</pre>
|
|
305
|
+
</ErrorMessage>
|
|
306
|
+
)}
|
|
307
|
+
</>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function ErrorMessage(props: React.PropsWithChildren) {
|
|
313
|
+
return <div className="jp-pluginmanager-error">{props.children}</div>;
|
|
314
|
+
}
|
package/style/base.css
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
.jp-pluginmanager {
|
|
7
|
+
display: grid;
|
|
8
|
+
grid-template-rows: min-content min-content 1fr;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.jp-pluginmanager-Header,
|
|
12
|
+
.jp-pluginmanager-Disclaimer {
|
|
13
|
+
padding: 8px;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.jp-pluginmanager-Disclaimer-checkbox {
|
|
17
|
+
position: relative;
|
|
18
|
+
top: 2px;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.jp-pluginmanager-AvailableList {
|
|
22
|
+
overflow: auto;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.jp-pluginmanager-AvailableList > .jp-sortable-table > thead {
|
|
26
|
+
position: sticky;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.jp-pluginmanager-PluginInUseMessage {
|
|
30
|
+
max-width: 550px;
|
|
31
|
+
}
|
package/style/index.css
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/*-----------------------------------------------------------------------------
|
|
2
|
+
| Copyright (c) Jupyter Development Team.
|
|
3
|
+
| Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|----------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */
|
|
7
|
+
@import url('~@lumino/widgets/style/index.css');
|
|
8
|
+
@import url('~@jupyterlab/ui-components/style/index.css');
|
|
9
|
+
@import url('~@jupyterlab/apputils/style/index.css');
|
|
10
|
+
@import url('~@jupyterlab/application/style/index.css');
|
|
11
|
+
@import url('./base.css');
|
package/style/index.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/*-----------------------------------------------------------------------------
|
|
2
|
+
| Copyright (c) Jupyter Development Team.
|
|
3
|
+
| Distributed under the terms of the Modified BSD License.
|
|
4
|
+
|----------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
/* This file was auto-generated by ensurePackage() in @jupyterlab/buildutils */
|
|
7
|
+
import '@lumino/widgets/style/index.js';
|
|
8
|
+
import '@jupyterlab/ui-components/style/index.js';
|
|
9
|
+
import '@jupyterlab/apputils/style/index.js';
|
|
10
|
+
import '@jupyterlab/application/style/index.js';
|
|
11
|
+
|
|
12
|
+
import './base.css';
|