@jupyterlab/imageviewer-extension 4.0.0-alpha.8 → 4.0.0-beta.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/package.json +10 -9
- package/src/index.ts +313 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jupyterlab/imageviewer-extension",
|
|
3
|
-
"version": "4.0.0-
|
|
3
|
+
"version": "4.0.0-beta.0",
|
|
4
4
|
"description": "JupyterLab - Image Widget Extension",
|
|
5
5
|
"homepage": "https://github.com/jupyterlab/jupyterlab",
|
|
6
6
|
"bugs": {
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"lib/*.js",
|
|
29
29
|
"schema/*.json",
|
|
30
30
|
"style/**/*.css",
|
|
31
|
-
"style/index.js"
|
|
31
|
+
"style/index.js",
|
|
32
|
+
"src/**/*.{ts,tsx}"
|
|
32
33
|
],
|
|
33
34
|
"scripts": {
|
|
34
35
|
"build": "tsc -b",
|
|
@@ -37,16 +38,16 @@
|
|
|
37
38
|
"watch": "tsc -b --watch"
|
|
38
39
|
},
|
|
39
40
|
"dependencies": {
|
|
40
|
-
"@jupyterlab/application": "^4.0.0-
|
|
41
|
-
"@jupyterlab/apputils": "^4.0.0-
|
|
42
|
-
"@jupyterlab/docregistry": "^4.0.0-
|
|
43
|
-
"@jupyterlab/imageviewer": "^4.0.0-
|
|
44
|
-
"@jupyterlab/translation": "^4.0.0-
|
|
41
|
+
"@jupyterlab/application": "^4.0.0-beta.0",
|
|
42
|
+
"@jupyterlab/apputils": "^4.0.0-beta.0",
|
|
43
|
+
"@jupyterlab/docregistry": "^4.0.0-beta.0",
|
|
44
|
+
"@jupyterlab/imageviewer": "^4.0.0-beta.0",
|
|
45
|
+
"@jupyterlab/translation": "^4.0.0-beta.0"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
48
|
"rimraf": "~3.0.0",
|
|
48
|
-
"typedoc": "~0.
|
|
49
|
-
"typescript": "~
|
|
49
|
+
"typedoc": "~0.23.25",
|
|
50
|
+
"typescript": "~5.0.2"
|
|
50
51
|
},
|
|
51
52
|
"publishConfig": {
|
|
52
53
|
"access": "public"
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
/**
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
* @module imageviewer-extension
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
ILayoutRestorer,
|
|
10
|
+
JupyterFrontEnd,
|
|
11
|
+
JupyterFrontEndPlugin
|
|
12
|
+
} from '@jupyterlab/application';
|
|
13
|
+
import { ICommandPalette, WidgetTracker } from '@jupyterlab/apputils';
|
|
14
|
+
import { DocumentRegistry, IDocumentWidget } from '@jupyterlab/docregistry';
|
|
15
|
+
import {
|
|
16
|
+
IImageTracker,
|
|
17
|
+
ImageViewer,
|
|
18
|
+
ImageViewerFactory
|
|
19
|
+
} from '@jupyterlab/imageviewer';
|
|
20
|
+
import { ITranslator } from '@jupyterlab/translation';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The command IDs used by the image widget plugin.
|
|
24
|
+
*/
|
|
25
|
+
namespace CommandIDs {
|
|
26
|
+
export const resetImage = 'imageviewer:reset-image';
|
|
27
|
+
|
|
28
|
+
export const zoomIn = 'imageviewer:zoom-in';
|
|
29
|
+
|
|
30
|
+
export const zoomOut = 'imageviewer:zoom-out';
|
|
31
|
+
|
|
32
|
+
export const flipHorizontal = 'imageviewer:flip-horizontal';
|
|
33
|
+
|
|
34
|
+
export const flipVertical = 'imageviewer:flip-vertical';
|
|
35
|
+
|
|
36
|
+
export const rotateClockwise = 'imageviewer:rotate-clockwise';
|
|
37
|
+
|
|
38
|
+
export const rotateCounterclockwise = 'imageviewer:rotate-counterclockwise';
|
|
39
|
+
|
|
40
|
+
export const invertColors = 'imageviewer:invert-colors';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The list of file types for images.
|
|
45
|
+
*/
|
|
46
|
+
const FILE_TYPES = ['png', 'gif', 'jpeg', 'bmp', 'ico', 'tiff'];
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The name of the factory that creates image widgets.
|
|
50
|
+
*/
|
|
51
|
+
const FACTORY = 'Image';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The name of the factory that creates image widgets.
|
|
55
|
+
*/
|
|
56
|
+
const TEXT_FACTORY = 'Image (Text)';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The list of file types for images with optional text modes.
|
|
60
|
+
*/
|
|
61
|
+
const TEXT_FILE_TYPES = ['svg', 'xbm'];
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* The test pattern for text file types in paths.
|
|
65
|
+
*/
|
|
66
|
+
const TEXT_FILE_REGEX = new RegExp(`[.](${TEXT_FILE_TYPES.join('|')})$`);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The image file handler extension.
|
|
70
|
+
*/
|
|
71
|
+
const plugin: JupyterFrontEndPlugin<IImageTracker> = {
|
|
72
|
+
activate,
|
|
73
|
+
id: '@jupyterlab/imageviewer-extension:plugin',
|
|
74
|
+
provides: IImageTracker,
|
|
75
|
+
requires: [ITranslator],
|
|
76
|
+
optional: [ICommandPalette, ILayoutRestorer],
|
|
77
|
+
autoStart: true
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Export the plugin as default.
|
|
82
|
+
*/
|
|
83
|
+
export default plugin;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Activate the image widget extension.
|
|
87
|
+
*/
|
|
88
|
+
function activate(
|
|
89
|
+
app: JupyterFrontEnd,
|
|
90
|
+
translator: ITranslator,
|
|
91
|
+
palette: ICommandPalette | null,
|
|
92
|
+
restorer: ILayoutRestorer | null
|
|
93
|
+
): IImageTracker {
|
|
94
|
+
const trans = translator.load('jupyterlab');
|
|
95
|
+
const namespace = 'image-widget';
|
|
96
|
+
|
|
97
|
+
function onWidgetCreated(
|
|
98
|
+
sender: any,
|
|
99
|
+
widget: IDocumentWidget<ImageViewer, DocumentRegistry.IModel>
|
|
100
|
+
) {
|
|
101
|
+
// Notify the widget tracker if restore data needs to update.
|
|
102
|
+
widget.context.pathChanged.connect(() => {
|
|
103
|
+
void tracker.save(widget);
|
|
104
|
+
});
|
|
105
|
+
void tracker.add(widget);
|
|
106
|
+
|
|
107
|
+
const types = app.docRegistry.getFileTypesForPath(widget.context.path);
|
|
108
|
+
|
|
109
|
+
if (types.length > 0) {
|
|
110
|
+
widget.title.icon = types[0].icon!;
|
|
111
|
+
widget.title.iconClass = types[0].iconClass ?? '';
|
|
112
|
+
widget.title.iconLabel = types[0].iconLabel ?? '';
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const factory = new ImageViewerFactory({
|
|
117
|
+
name: FACTORY,
|
|
118
|
+
label: trans.__('Image'),
|
|
119
|
+
modelName: 'base64',
|
|
120
|
+
fileTypes: [...FILE_TYPES, ...TEXT_FILE_TYPES],
|
|
121
|
+
defaultFor: FILE_TYPES,
|
|
122
|
+
readOnly: true
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const textFactory = new ImageViewerFactory({
|
|
126
|
+
name: TEXT_FACTORY,
|
|
127
|
+
label: trans.__('Image (Text)'),
|
|
128
|
+
modelName: 'text',
|
|
129
|
+
fileTypes: TEXT_FILE_TYPES,
|
|
130
|
+
defaultFor: TEXT_FILE_TYPES,
|
|
131
|
+
readOnly: true
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
[factory, textFactory].forEach(factory => {
|
|
135
|
+
app.docRegistry.addWidgetFactory(factory);
|
|
136
|
+
factory.widgetCreated.connect(onWidgetCreated);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const tracker = new WidgetTracker<IDocumentWidget<ImageViewer>>({
|
|
140
|
+
namespace
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (restorer) {
|
|
144
|
+
// Handle state restoration.
|
|
145
|
+
void restorer.restore(tracker, {
|
|
146
|
+
command: 'docmanager:open',
|
|
147
|
+
args: widget => ({
|
|
148
|
+
path: widget.context.path,
|
|
149
|
+
factory: TEXT_FILE_REGEX.test(widget.context.path)
|
|
150
|
+
? TEXT_FACTORY
|
|
151
|
+
: FACTORY
|
|
152
|
+
}),
|
|
153
|
+
name: widget => widget.context.path
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
addCommands(app, tracker, translator);
|
|
158
|
+
|
|
159
|
+
if (palette) {
|
|
160
|
+
const category = trans.__('Image Viewer');
|
|
161
|
+
[
|
|
162
|
+
CommandIDs.zoomIn,
|
|
163
|
+
CommandIDs.zoomOut,
|
|
164
|
+
CommandIDs.resetImage,
|
|
165
|
+
CommandIDs.rotateClockwise,
|
|
166
|
+
CommandIDs.rotateCounterclockwise,
|
|
167
|
+
CommandIDs.flipHorizontal,
|
|
168
|
+
CommandIDs.flipVertical,
|
|
169
|
+
CommandIDs.invertColors
|
|
170
|
+
].forEach(command => {
|
|
171
|
+
palette.addItem({ command, category });
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return tracker;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Add the commands for the image widget.
|
|
180
|
+
*/
|
|
181
|
+
export function addCommands(
|
|
182
|
+
app: JupyterFrontEnd,
|
|
183
|
+
tracker: IImageTracker,
|
|
184
|
+
translator: ITranslator
|
|
185
|
+
): void {
|
|
186
|
+
const trans = translator.load('jupyterlab');
|
|
187
|
+
const { commands, shell } = app;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Whether there is an active image viewer.
|
|
191
|
+
*/
|
|
192
|
+
function isEnabled(): boolean {
|
|
193
|
+
return (
|
|
194
|
+
tracker.currentWidget !== null &&
|
|
195
|
+
tracker.currentWidget === shell.currentWidget
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
commands.addCommand('imageviewer:zoom-in', {
|
|
200
|
+
execute: zoomIn,
|
|
201
|
+
label: trans.__('Zoom In'),
|
|
202
|
+
isEnabled
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
commands.addCommand('imageviewer:zoom-out', {
|
|
206
|
+
execute: zoomOut,
|
|
207
|
+
label: trans.__('Zoom Out'),
|
|
208
|
+
isEnabled
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
commands.addCommand('imageviewer:reset-image', {
|
|
212
|
+
execute: resetImage,
|
|
213
|
+
label: trans.__('Reset Image'),
|
|
214
|
+
isEnabled
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
commands.addCommand('imageviewer:rotate-clockwise', {
|
|
218
|
+
execute: rotateClockwise,
|
|
219
|
+
label: trans.__('Rotate Clockwise'),
|
|
220
|
+
isEnabled
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
commands.addCommand('imageviewer:rotate-counterclockwise', {
|
|
224
|
+
execute: rotateCounterclockwise,
|
|
225
|
+
label: trans.__('Rotate Counterclockwise'),
|
|
226
|
+
isEnabled
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
commands.addCommand('imageviewer:flip-horizontal', {
|
|
230
|
+
execute: flipHorizontal,
|
|
231
|
+
label: trans.__('Flip image horizontally'),
|
|
232
|
+
isEnabled
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
commands.addCommand('imageviewer:flip-vertical', {
|
|
236
|
+
execute: flipVertical,
|
|
237
|
+
label: trans.__('Flip image vertically'),
|
|
238
|
+
isEnabled
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
commands.addCommand('imageviewer:invert-colors', {
|
|
242
|
+
execute: invertColors,
|
|
243
|
+
label: trans.__('Invert Colors'),
|
|
244
|
+
isEnabled
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
function zoomIn(): void {
|
|
248
|
+
const widget = tracker.currentWidget?.content;
|
|
249
|
+
|
|
250
|
+
if (widget) {
|
|
251
|
+
widget.scale = widget.scale > 1 ? widget.scale + 0.5 : widget.scale * 2;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function zoomOut(): void {
|
|
256
|
+
const widget = tracker.currentWidget?.content;
|
|
257
|
+
|
|
258
|
+
if (widget) {
|
|
259
|
+
widget.scale = widget.scale > 1 ? widget.scale - 0.5 : widget.scale / 2;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function resetImage(): void {
|
|
264
|
+
const widget = tracker.currentWidget?.content;
|
|
265
|
+
|
|
266
|
+
if (widget) {
|
|
267
|
+
widget.scale = 1;
|
|
268
|
+
widget.colorinversion = 0;
|
|
269
|
+
widget.resetRotationFlip();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function rotateClockwise(): void {
|
|
274
|
+
const widget = tracker.currentWidget?.content;
|
|
275
|
+
|
|
276
|
+
if (widget) {
|
|
277
|
+
widget.rotateClockwise();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function rotateCounterclockwise(): void {
|
|
282
|
+
const widget = tracker.currentWidget?.content;
|
|
283
|
+
|
|
284
|
+
if (widget) {
|
|
285
|
+
widget.rotateCounterclockwise();
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function flipHorizontal(): void {
|
|
290
|
+
const widget = tracker.currentWidget?.content;
|
|
291
|
+
|
|
292
|
+
if (widget) {
|
|
293
|
+
widget.flipHorizontal();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function flipVertical(): void {
|
|
298
|
+
const widget = tracker.currentWidget?.content;
|
|
299
|
+
|
|
300
|
+
if (widget) {
|
|
301
|
+
widget.flipVertical();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function invertColors(): void {
|
|
306
|
+
const widget = tracker.currentWidget?.content;
|
|
307
|
+
|
|
308
|
+
if (widget) {
|
|
309
|
+
widget.colorinversion += 1;
|
|
310
|
+
widget.colorinversion %= 2;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|