jupyterlab_advanced_image_viewer_extension 1.0.5 → 1.0.7

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/README.md CHANGED
@@ -10,11 +10,15 @@
10
10
 
11
11
  A JupyterLab 4 extension that makes the built-in image viewer interactive. Images open fitted to the panel just like the stock viewer, then you can zoom with the wheel, pan by dragging, and step through a folder of images with the arrow keys - all in a single viewer tab.
12
12
 
13
+ ![Advanced image viewer](https://raw.githubusercontent.com/stellarshenson/jupyterlab_advanced_image_viewer_extension/main/.resources/screenshot.png)
14
+
15
+ The viewer toolbar adds zoom in / out / fit controls and a `help` link that lists every keybinding.
16
+
13
17
  ## Features
14
18
 
15
19
  - **Fit to screen** - every image opens scaled to fit the panel (same result as the stock viewer), and reset returns to that fit
16
20
  - **Wheel zoom** - scroll to zoom in and out, anchored at the cursor; zoom out as far as you like
17
- - **Drag to pan** - hold and drag to move around a zoomed-in image
21
+ - **Drag to pan** - hold and drag to move the image at any zoom (canvas-style); the grab cursor is always available and Fit recenters
18
22
  - **Toolbar controls** - zoom in, zoom out, and reset-to-fit buttons on the viewer toolbar
19
23
  - **Arrow-key navigation** - Left and Right open the previous and next image in the same folder, advancing within one viewer instead of opening new tabs
20
24
 
@@ -7,7 +7,6 @@ export declare class ViewerController {
7
7
  private panning;
8
8
  private start;
9
9
  private zoomStep;
10
- private ro;
11
10
  private disposed;
12
11
  constructor(host: HTMLElement, img: HTMLImageElement, zoomStep: number);
13
12
  setZoomStep(zoomStep: number): void;
@@ -18,12 +17,9 @@ export declare class ViewerController {
18
17
  private onWheel;
19
18
  zoomIn(): void;
20
19
  zoomOut(): void;
21
- private canPan;
22
- private clampPan;
23
20
  private onDown;
24
21
  private onMove;
25
22
  private onUp;
26
- private onResize;
27
23
  private updateCursor;
28
24
  private apply;
29
25
  dispose(): void;
package/lib/controller.js CHANGED
@@ -23,9 +23,6 @@ export class ViewerController {
23
23
  this.zoomAt(factor, e.clientX - r.left, e.clientY - r.top);
24
24
  };
25
25
  this.onDown = (e) => {
26
- if (!this.canPan()) {
27
- return;
28
- }
29
26
  e.preventDefault();
30
27
  this.panning = true;
31
28
  this.start = { x: e.clientX, y: e.clientY, tx: this.tx, ty: this.ty };
@@ -39,7 +36,6 @@ export class ViewerController {
39
36
  }
40
37
  this.tx = this.start.tx + (e.clientX - this.start.x);
41
38
  this.ty = this.start.ty + (e.clientY - this.start.y);
42
- this.clampPan();
43
39
  this.apply();
44
40
  };
45
41
  this.onUp = () => {
@@ -65,8 +61,6 @@ export class ViewerController {
65
61
  this.layer = layer;
66
62
  this.host.addEventListener('wheel', this.onWheel, { passive: false });
67
63
  this.host.addEventListener('mousedown', this.onDown);
68
- this.ro = new ResizeObserver(() => this.onResize());
69
- this.ro.observe(this.host);
70
64
  this.apply();
71
65
  }
72
66
  setZoomStep(zoomStep) {
@@ -99,7 +93,6 @@ export class ViewerController {
99
93
  this.tx = ax - cx - k * (ax - cx - this.tx);
100
94
  this.ty = ay - cy - k * (ay - cy - this.ty);
101
95
  this.s = sNew;
102
- this.clampPan();
103
96
  this.apply();
104
97
  }
105
98
  zoomIn() {
@@ -110,24 +103,10 @@ export class ViewerController {
110
103
  const { vw, vh } = this.viewport();
111
104
  this.zoomAt(1 / (1 + this.zoomStep), vw / 2, vh / 2);
112
105
  }
113
- canPan() {
114
- return this.s > 1;
115
- }
116
- // Bound the translate to the overflow implied by the zoom so the image
117
- // cannot be panned out of view. Range collapses to 0 as s approaches 1.
118
- clampPan() {
119
- const { vw, vh } = this.viewport();
120
- const mx = (Math.max(this.s, 1) - 1) * vw * 0.5;
121
- const my = (Math.max(this.s, 1) - 1) * vh * 0.5;
122
- this.tx = Math.min(mx, Math.max(-mx, this.tx));
123
- this.ty = Math.min(my, Math.max(-my, this.ty));
124
- }
125
- onResize() {
126
- this.clampPan();
127
- this.apply();
128
- }
106
+ // Pan is always available (canvas-style): show grab normally, grabbing
107
+ // while a drag is in progress (apply() re-runs on every pan move).
129
108
  updateCursor() {
130
- this.host.style.cursor = this.canPan() ? 'grab' : 'default';
109
+ this.host.style.cursor = this.panning ? 'grabbing' : 'grab';
131
110
  }
132
111
  apply() {
133
112
  this.layer.style.transformOrigin = 'center center';
@@ -139,7 +118,6 @@ export class ViewerController {
139
118
  return;
140
119
  }
141
120
  this.disposed = true;
142
- this.ro.disconnect();
143
121
  this.host.removeEventListener('wheel', this.onWheel);
144
122
  this.host.removeEventListener('mousedown', this.onDown);
145
123
  window.removeEventListener('mousemove', this.onMove);
package/lib/index.js CHANGED
@@ -52,16 +52,16 @@ const plugin = {
52
52
  '<p class="jp-AdvancedImageViewer-help-section">This extension</p>',
53
53
  '<ul>',
54
54
  '<li><b>Wheel up / down</b> - zoom in / out at the cursor</li>',
55
- '<li><b>Click and drag</b> - pan a zoomed-in image</li>',
56
- '<li><b>+ / - / Fit</b> - zoom in, zoom out, reset to fit</li>',
55
+ '<li><b>Click and drag</b> - pan the image at any zoom</li>',
56
+ '<li><b>Toolbar buttons</b> - zoom in, zoom out, reset to fit</li>',
57
57
  '<li><b>Left / Right arrows</b> - previous / next image in the folder</li>',
58
58
  '</ul>',
59
59
  '<p class="jp-AdvancedImageViewer-help-section">Standard viewer keys (compose with the above)</p>',
60
60
  '<ul>',
61
+ '<li><b>= / - / 0</b> - zoom in / out / reset</li>',
61
62
  '<li><b>] / [</b> - rotate clockwise / counter-clockwise</li>',
62
63
  '<li><b>H / V</b> - flip horizontal / vertical</li>',
63
64
  '<li><b>I</b> - invert colours</li>',
64
- '<li><b>= / - / 0</b> - zoom in / out / reset</li>',
65
65
  '</ul>'
66
66
  ].join('');
67
67
  void showDialog({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jupyterlab_advanced_image_viewer_extension",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "JupyterLab extension for advanced image viewing: fit-to-screen, cursor-anchored wheel zoom, drag-to-pan, and Left/Right arrow keys to move to the previous and next image in a folder",
5
5
  "keywords": [
6
6
  "jupyter",
package/src/controller.ts CHANGED
@@ -19,7 +19,6 @@ export class ViewerController {
19
19
  private panning = false;
20
20
  private start = { x: 0, y: 0, tx: 0, ty: 0 };
21
21
  private zoomStep: number;
22
- private ro: ResizeObserver;
23
22
  private disposed = false;
24
23
 
25
24
  constructor(host: HTMLElement, img: HTMLImageElement, zoomStep: number) {
@@ -40,8 +39,6 @@ export class ViewerController {
40
39
  this.layer = layer;
41
40
  this.host.addEventListener('wheel', this.onWheel, { passive: false });
42
41
  this.host.addEventListener('mousedown', this.onDown);
43
- this.ro = new ResizeObserver(() => this.onResize());
44
- this.ro.observe(this.host);
45
42
  this.apply();
46
43
  }
47
44
 
@@ -79,7 +76,6 @@ export class ViewerController {
79
76
  this.tx = ax - cx - k * (ax - cx - this.tx);
80
77
  this.ty = ay - cy - k * (ay - cy - this.ty);
81
78
  this.s = sNew;
82
- this.clampPan();
83
79
  this.apply();
84
80
  }
85
81
 
@@ -100,24 +96,7 @@ export class ViewerController {
100
96
  this.zoomAt(1 / (1 + this.zoomStep), vw / 2, vh / 2);
101
97
  }
102
98
 
103
- private canPan(): boolean {
104
- return this.s > 1;
105
- }
106
-
107
- // Bound the translate to the overflow implied by the zoom so the image
108
- // cannot be panned out of view. Range collapses to 0 as s approaches 1.
109
- private clampPan(): void {
110
- const { vw, vh } = this.viewport();
111
- const mx = (Math.max(this.s, 1) - 1) * vw * 0.5;
112
- const my = (Math.max(this.s, 1) - 1) * vh * 0.5;
113
- this.tx = Math.min(mx, Math.max(-mx, this.tx));
114
- this.ty = Math.min(my, Math.max(-my, this.ty));
115
- }
116
-
117
99
  private onDown = (e: MouseEvent): void => {
118
- if (!this.canPan()) {
119
- return;
120
- }
121
100
  e.preventDefault();
122
101
  this.panning = true;
123
102
  this.start = { x: e.clientX, y: e.clientY, tx: this.tx, ty: this.ty };
@@ -132,7 +111,6 @@ export class ViewerController {
132
111
  }
133
112
  this.tx = this.start.tx + (e.clientX - this.start.x);
134
113
  this.ty = this.start.ty + (e.clientY - this.start.y);
135
- this.clampPan();
136
114
  this.apply();
137
115
  };
138
116
 
@@ -143,13 +121,10 @@ export class ViewerController {
143
121
  window.removeEventListener('mouseup', this.onUp);
144
122
  };
145
123
 
146
- private onResize(): void {
147
- this.clampPan();
148
- this.apply();
149
- }
150
-
124
+ // Pan is always available (canvas-style): show grab normally, grabbing
125
+ // while a drag is in progress (apply() re-runs on every pan move).
151
126
  private updateCursor(): void {
152
- this.host.style.cursor = this.canPan() ? 'grab' : 'default';
127
+ this.host.style.cursor = this.panning ? 'grabbing' : 'grab';
153
128
  }
154
129
 
155
130
  private apply(): void {
@@ -163,7 +138,6 @@ export class ViewerController {
163
138
  return;
164
139
  }
165
140
  this.disposed = true;
166
- this.ro.disconnect();
167
141
  this.host.removeEventListener('wheel', this.onWheel);
168
142
  this.host.removeEventListener('mousedown', this.onDown);
169
143
  window.removeEventListener('mousemove', this.onMove);
package/src/index.ts CHANGED
@@ -89,16 +89,16 @@ const plugin: JupyterFrontEndPlugin<void> = {
89
89
  '<p class="jp-AdvancedImageViewer-help-section">This extension</p>',
90
90
  '<ul>',
91
91
  '<li><b>Wheel up / down</b> - zoom in / out at the cursor</li>',
92
- '<li><b>Click and drag</b> - pan a zoomed-in image</li>',
93
- '<li><b>+ / - / Fit</b> - zoom in, zoom out, reset to fit</li>',
92
+ '<li><b>Click and drag</b> - pan the image at any zoom</li>',
93
+ '<li><b>Toolbar buttons</b> - zoom in, zoom out, reset to fit</li>',
94
94
  '<li><b>Left / Right arrows</b> - previous / next image in the folder</li>',
95
95
  '</ul>',
96
96
  '<p class="jp-AdvancedImageViewer-help-section">Standard viewer keys (compose with the above)</p>',
97
97
  '<ul>',
98
+ '<li><b>= / - / 0</b> - zoom in / out / reset</li>',
98
99
  '<li><b>] / [</b> - rotate clockwise / counter-clockwise</li>',
99
100
  '<li><b>H / V</b> - flip horizontal / vertical</li>',
100
101
  '<li><b>I</b> - invert colours</li>',
101
- '<li><b>= / - / 0</b> - zoom in / out / reset</li>',
102
102
  '</ul>'
103
103
  ].join('');
104
104
  void showDialog({