@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.
Files changed (2) hide show
  1. package/package.json +10 -9
  2. 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-alpha.8",
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-alpha.8",
41
- "@jupyterlab/apputils": "^4.0.0-alpha.8",
42
- "@jupyterlab/docregistry": "^4.0.0-alpha.8",
43
- "@jupyterlab/imageviewer": "^4.0.0-alpha.8",
44
- "@jupyterlab/translation": "^4.0.0-alpha.8"
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.22.10",
49
- "typescript": "~4.5.2"
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
+ }