retold-remote 0.0.1

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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/css/retold-remote.css +83 -0
  3. package/html/codejar.js +511 -0
  4. package/html/index.html +23 -0
  5. package/package.json +68 -0
  6. package/server.js +43 -0
  7. package/source/Pict-Application-RetoldRemote-Configuration.json +7 -0
  8. package/source/Pict-Application-RetoldRemote.js +622 -0
  9. package/source/Pict-RetoldRemote-Bundle.js +14 -0
  10. package/source/cli/RetoldRemote-CLI-Program.js +15 -0
  11. package/source/cli/RetoldRemote-CLI-Run.js +3 -0
  12. package/source/cli/RetoldRemote-Server-Setup.js +257 -0
  13. package/source/cli/commands/RetoldRemote-Command-Serve.js +87 -0
  14. package/source/providers/Pict-Provider-GalleryFilterSort.js +597 -0
  15. package/source/providers/Pict-Provider-GalleryNavigation.js +819 -0
  16. package/source/providers/Pict-Provider-RetoldRemote.js +273 -0
  17. package/source/providers/Pict-Provider-RetoldRemoteIcons.js +640 -0
  18. package/source/providers/Pict-Provider-RetoldRemoteTheme.js +879 -0
  19. package/source/server/RetoldRemote-MediaService.js +536 -0
  20. package/source/server/RetoldRemote-PathRegistry.js +121 -0
  21. package/source/server/RetoldRemote-ThumbnailCache.js +89 -0
  22. package/source/server/RetoldRemote-ToolDetector.js +78 -0
  23. package/source/views/PictView-Remote-Gallery.js +1437 -0
  24. package/source/views/PictView-Remote-ImageViewer.js +363 -0
  25. package/source/views/PictView-Remote-Layout.js +420 -0
  26. package/source/views/PictView-Remote-MediaViewer.js +530 -0
  27. package/source/views/PictView-Remote-SettingsPanel.js +318 -0
  28. package/source/views/PictView-Remote-TopBar.js +206 -0
  29. package/web-application/codejar.js +511 -0
  30. package/web-application/css/retold-remote.css +83 -0
  31. package/web-application/index.html +23 -0
  32. package/web-application/js/pict.min.js +12 -0
  33. package/web-application/js/pict.min.js.map +1 -0
  34. package/web-application/retold-remote.compatible.js +5764 -0
  35. package/web-application/retold-remote.compatible.js.map +1 -0
  36. package/web-application/retold-remote.compatible.min.js +120 -0
  37. package/web-application/retold-remote.compatible.min.js.map +1 -0
  38. package/web-application/retold-remote.js +5763 -0
  39. package/web-application/retold-remote.js.map +1 -0
  40. package/web-application/retold-remote.min.js +120 -0
  41. package/web-application/retold-remote.min.js.map +1 -0
@@ -0,0 +1,363 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _ViewConfiguration =
4
+ {
5
+ ViewIdentifier: "RetoldRemote-ImageViewer",
6
+ DefaultRenderable: "RetoldRemote-ImageViewer",
7
+ DefaultDestinationAddress: "#RetoldRemote-Viewer-Container",
8
+ AutoRender: false,
9
+
10
+ CSS: /*css*/`
11
+ #RetoldRemote-ImageViewer-Img
12
+ {
13
+ image-orientation: from-image;
14
+ transition: width 0.15s ease, height 0.15s ease;
15
+ display: block;
16
+ }
17
+ .retold-remote-fit-indicator
18
+ {
19
+ position: absolute;
20
+ top: 50%;
21
+ left: 50%;
22
+ transform: translate(-50%, -50%);
23
+ background: rgba(0, 0, 0, 0.7);
24
+ color: #fff;
25
+ padding: 8px 18px;
26
+ border-radius: 6px;
27
+ font-size: 0.82rem;
28
+ pointer-events: none;
29
+ z-index: 20;
30
+ opacity: 0;
31
+ transition: opacity 0.3s ease;
32
+ white-space: nowrap;
33
+ }
34
+ .retold-remote-fit-indicator.visible
35
+ {
36
+ opacity: 1;
37
+ }
38
+ `
39
+ };
40
+
41
+ class RetoldRemoteImageViewerView extends libPictView
42
+ {
43
+ constructor(pFable, pOptions, pServiceHash)
44
+ {
45
+ super(pFable, pOptions, pServiceHash);
46
+
47
+ this._zoomLevel = 1;
48
+ this._naturalWidth = 0;
49
+ this._naturalHeight = 0;
50
+ this._resizeHandler = null;
51
+ this._indicatorTimeout = null;
52
+ }
53
+
54
+ /**
55
+ * Called when the image finishes loading. Captures the natural
56
+ * dimensions and applies the current fit mode.
57
+ */
58
+ initImage()
59
+ {
60
+ let tmpImg = document.getElementById('RetoldRemote-ImageViewer-Img');
61
+ if (!tmpImg)
62
+ {
63
+ return;
64
+ }
65
+
66
+ this._naturalWidth = tmpImg.naturalWidth;
67
+ this._naturalHeight = tmpImg.naturalHeight;
68
+ this._zoomLevel = 1;
69
+
70
+ this._applyDisplay();
71
+
72
+ // Recalculate on window resize
73
+ if (this._resizeHandler)
74
+ {
75
+ window.removeEventListener('resize', this._resizeHandler);
76
+ }
77
+
78
+ let tmpSelf = this;
79
+ let tmpResizeTimer = null;
80
+ this._resizeHandler = function ()
81
+ {
82
+ clearTimeout(tmpResizeTimer);
83
+ tmpResizeTimer = setTimeout(function ()
84
+ {
85
+ tmpSelf._applyDisplay();
86
+ }, 100);
87
+ };
88
+ window.addEventListener('resize', this._resizeHandler);
89
+ }
90
+
91
+ /**
92
+ * Get the current fit mode from AppData.
93
+ *
94
+ * @returns {string} 'fit' | 'auto' | 'original'
95
+ */
96
+ _getFitMode()
97
+ {
98
+ let tmpRemote = this.pict.AppData.RetoldRemote;
99
+ return tmpRemote.ImageFitMode || 'auto';
100
+ }
101
+
102
+ /**
103
+ * Set the fit mode and persist it.
104
+ *
105
+ * @param {string} pMode - 'fit' | 'auto' | 'original'
106
+ */
107
+ setFitMode(pMode)
108
+ {
109
+ let tmpRemote = this.pict.AppData.RetoldRemote;
110
+ tmpRemote.ImageFitMode = pMode;
111
+ this._zoomLevel = 1;
112
+ this._applyDisplay();
113
+
114
+ if (this.pict.PictApplication && this.pict.PictApplication.saveSettings)
115
+ {
116
+ this.pict.PictApplication.saveSettings();
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Cycle through fit modes: fit → auto → original → fit
122
+ */
123
+ cycleFitMode()
124
+ {
125
+ let tmpMode = this._getFitMode();
126
+ let tmpNext;
127
+
128
+ switch (tmpMode)
129
+ {
130
+ case 'fit':
131
+ tmpNext = 'auto';
132
+ break;
133
+ case 'auto':
134
+ tmpNext = 'original';
135
+ break;
136
+ default:
137
+ tmpNext = 'fit';
138
+ break;
139
+ }
140
+
141
+ this.setFitMode(tmpNext);
142
+ this._showFitModeIndicator(tmpNext);
143
+ }
144
+
145
+ /**
146
+ * Toggle between 1× and 2× zoom.
147
+ */
148
+ toggleZoom()
149
+ {
150
+ if (!this._naturalWidth)
151
+ {
152
+ return;
153
+ }
154
+
155
+ if (this._zoomLevel === 1)
156
+ {
157
+ this._zoomLevel = 2;
158
+ }
159
+ else
160
+ {
161
+ this._zoomLevel = 1;
162
+ }
163
+
164
+ this._applyDisplay();
165
+ }
166
+
167
+ zoomIn()
168
+ {
169
+ this._zoomLevel = Math.min(this._zoomLevel * 1.25, 8);
170
+ this._applyDisplay();
171
+ }
172
+
173
+ zoomOut()
174
+ {
175
+ this._zoomLevel = Math.max(this._zoomLevel / 1.25, 0.25);
176
+ this._applyDisplay();
177
+ }
178
+
179
+ zoomReset()
180
+ {
181
+ this._zoomLevel = 1;
182
+ this._applyDisplay();
183
+ }
184
+
185
+ /**
186
+ * Core display method. Calculates image width/height from the
187
+ * current fit mode and zoom level, then sets them explicitly so
188
+ * the container can scroll naturally.
189
+ */
190
+ _applyDisplay()
191
+ {
192
+ let tmpImg = document.getElementById('RetoldRemote-ImageViewer-Img');
193
+ if (!tmpImg || !this._naturalWidth)
194
+ {
195
+ return;
196
+ }
197
+
198
+ let tmpContainer = tmpImg.parentElement;
199
+ if (!tmpContainer)
200
+ {
201
+ return;
202
+ }
203
+
204
+ let tmpBase = this._getBaseSize(tmpContainer);
205
+ let tmpDisplayW = Math.round(tmpBase.width * this._zoomLevel);
206
+ let tmpDisplayH = Math.round(tmpBase.height * this._zoomLevel);
207
+
208
+ // Clear the initial inline constraints from _buildImageHTML
209
+ tmpImg.style.maxWidth = 'none';
210
+ tmpImg.style.maxHeight = 'none';
211
+ tmpImg.style.objectFit = '';
212
+
213
+ tmpImg.style.width = tmpDisplayW + 'px';
214
+ tmpImg.style.height = tmpDisplayH + 'px';
215
+ tmpImg.style.transform = '';
216
+
217
+ // Update cursor based on whether zoomed
218
+ if (this._zoomLevel > 1 || (tmpDisplayW > tmpContainer.clientWidth || tmpDisplayH > tmpContainer.clientHeight))
219
+ {
220
+ tmpImg.style.cursor = 'zoom-out';
221
+ }
222
+ else
223
+ {
224
+ tmpImg.style.cursor = 'zoom-in';
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Calculate the base display size (before zoom) for the current
230
+ * fit mode.
231
+ *
232
+ * @param {HTMLElement} pContainer - The viewer body element
233
+ * @returns {{ width: number, height: number }}
234
+ */
235
+ _getBaseSize(pContainer)
236
+ {
237
+ let tmpNW = this._naturalWidth;
238
+ let tmpNH = this._naturalHeight;
239
+ let tmpCW = pContainer.clientWidth;
240
+ let tmpCH = pContainer.clientHeight;
241
+
242
+ if (!tmpCW || !tmpCH)
243
+ {
244
+ return { width: tmpNW, height: tmpNH };
245
+ }
246
+
247
+ let tmpAspect = tmpNW / tmpNH;
248
+ let tmpMode = this._getFitMode();
249
+
250
+ switch (tmpMode)
251
+ {
252
+ case 'fit':
253
+ {
254
+ // Always scale to fill viewport (contain)
255
+ let tmpFitW = tmpCW;
256
+ let tmpFitH = tmpCW / tmpAspect;
257
+
258
+ if (tmpFitH > tmpCH)
259
+ {
260
+ tmpFitH = tmpCH;
261
+ tmpFitW = tmpCH * tmpAspect;
262
+ }
263
+
264
+ return { width: tmpFitW, height: tmpFitH };
265
+ }
266
+
267
+ case 'auto':
268
+ {
269
+ // Original size if smaller, fit if larger
270
+ if (tmpNW <= tmpCW && tmpNH <= tmpCH)
271
+ {
272
+ return { width: tmpNW, height: tmpNH };
273
+ }
274
+
275
+ // Scale down to fit
276
+ let tmpFitW = tmpCW;
277
+ let tmpFitH = tmpCW / tmpAspect;
278
+
279
+ if (tmpFitH > tmpCH)
280
+ {
281
+ tmpFitH = tmpCH;
282
+ tmpFitW = tmpCH * tmpAspect;
283
+ }
284
+
285
+ return { width: tmpFitW, height: tmpFitH };
286
+ }
287
+
288
+ case 'original':
289
+ default:
290
+ {
291
+ // Always native resolution
292
+ return { width: tmpNW, height: tmpNH };
293
+ }
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Show a brief overlay label indicating the current fit mode.
299
+ *
300
+ * @param {string} pMode - The mode identifier
301
+ */
302
+ _showFitModeIndicator(pMode)
303
+ {
304
+ let tmpLabels =
305
+ {
306
+ 'fit': 'Fit to Window',
307
+ 'auto': 'Original if Smaller',
308
+ 'original': 'Original Size'
309
+ };
310
+
311
+ let tmpLabel = tmpLabels[pMode] || pMode;
312
+
313
+ // Create or reuse the indicator element
314
+ let tmpIndicator = document.getElementById('RetoldRemote-FitIndicator');
315
+ if (!tmpIndicator)
316
+ {
317
+ tmpIndicator = document.createElement('div');
318
+ tmpIndicator.id = 'RetoldRemote-FitIndicator';
319
+ tmpIndicator.className = 'retold-remote-fit-indicator';
320
+
321
+ let tmpContainer = document.querySelector('.retold-remote-viewer-body');
322
+ if (tmpContainer)
323
+ {
324
+ tmpContainer.appendChild(tmpIndicator);
325
+ }
326
+ }
327
+
328
+ tmpIndicator.textContent = tmpLabel;
329
+ tmpIndicator.classList.add('visible');
330
+
331
+ if (this._indicatorTimeout)
332
+ {
333
+ clearTimeout(this._indicatorTimeout);
334
+ }
335
+
336
+ this._indicatorTimeout = setTimeout(function ()
337
+ {
338
+ tmpIndicator.classList.remove('visible');
339
+ }, 1200);
340
+ }
341
+
342
+ /**
343
+ * Clean up resize handler when navigating away.
344
+ */
345
+ cleanup()
346
+ {
347
+ if (this._resizeHandler)
348
+ {
349
+ window.removeEventListener('resize', this._resizeHandler);
350
+ this._resizeHandler = null;
351
+ }
352
+
353
+ if (this._indicatorTimeout)
354
+ {
355
+ clearTimeout(this._indicatorTimeout);
356
+ this._indicatorTimeout = null;
357
+ }
358
+ }
359
+ }
360
+
361
+ RetoldRemoteImageViewerView.default_configuration = _ViewConfiguration;
362
+
363
+ module.exports = RetoldRemoteImageViewerView;