retold-remote 0.0.5 → 0.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/css/retold-remote.css +3 -0
- package/docs/README.md +181 -0
- package/docs/_cover.md +14 -0
- package/docs/_sidebar.md +10 -0
- package/docs/_topbar.md +3 -0
- package/docs/audio-viewer.md +133 -0
- package/docs/ebook-reader.md +90 -0
- package/docs/image-viewer.md +90 -0
- package/docs/server-setup.md +262 -0
- package/docs/video-viewer.md +134 -0
- package/html/docs.html +59 -0
- package/html/index.html +1 -1
- package/package.json +20 -6
- package/source/Pict-Application-RetoldRemote.js +122 -2
- package/source/RetoldRemote-ExtensionMaps.js +33 -0
- package/source/cli/RetoldRemote-Server-Setup.js +211 -67
- package/source/cli/commands/RetoldRemote-Command-Serve.js +5 -26
- package/source/providers/Pict-Provider-AISortManager.js +456 -0
- package/source/providers/Pict-Provider-CollectionManager.js +1200 -0
- package/source/providers/Pict-Provider-FormattingUtilities.js +109 -0
- package/source/providers/Pict-Provider-GalleryFilterSort.js +2 -11
- package/source/providers/Pict-Provider-GalleryNavigation.js +187 -409
- package/source/providers/Pict-Provider-RetoldRemoteIcons.js +52 -0
- package/source/providers/Pict-Provider-ToastNotification.js +96 -0
- package/source/providers/keyboard-handlers/KeyHandler-AudioExplorer.js +88 -0
- package/source/providers/keyboard-handlers/KeyHandler-Gallery.js +190 -0
- package/source/providers/keyboard-handlers/KeyHandler-Sidebar.js +65 -0
- package/source/providers/keyboard-handlers/KeyHandler-VideoExplorer.js +57 -0
- package/source/providers/keyboard-handlers/KeyHandler-Viewer.js +197 -0
- package/source/server/RetoldRemote-AISortService.js +879 -0
- package/source/server/RetoldRemote-ArchiveService.js +2 -12
- package/source/server/RetoldRemote-AudioWaveformService.js +7 -16
- package/source/server/RetoldRemote-CollectionService.js +843 -0
- package/source/server/RetoldRemote-EbookService.js +7 -16
- package/source/server/RetoldRemote-FileOperationService.js +560 -0
- package/source/server/RetoldRemote-MediaService.js +15 -14
- package/source/server/RetoldRemote-MetadataCache.js +411 -0
- package/source/server/RetoldRemote-ParimeCache.js +349 -0
- package/source/server/RetoldRemote-ThumbnailCache.js +52 -20
- package/source/server/RetoldRemote-VideoFrameService.js +7 -15
- package/source/views/PictView-Remote-AudioExplorer.js +10 -43
- package/source/views/PictView-Remote-CollectionsPanel.js +1486 -0
- package/source/views/PictView-Remote-Gallery.js +237 -44
- package/source/views/PictView-Remote-ImageViewer.js +1 -34
- package/source/views/PictView-Remote-Layout.js +411 -20
- package/source/views/PictView-Remote-MediaViewer.js +311 -28
- package/source/views/PictView-Remote-SettingsPanel.js +275 -133
- package/source/views/PictView-Remote-TopBar.js +701 -14
- package/source/views/PictView-Remote-VLCSetup.js +8 -39
- package/source/views/PictView-Remote-VideoExplorer.js +20 -54
- package/web-application/css/docuserve.css +73 -0
- package/web-application/css/retold-remote.css +3 -0
- package/web-application/docs/README.md +181 -0
- package/web-application/docs/_cover.md +14 -0
- package/web-application/docs/_sidebar.md +10 -0
- package/web-application/docs/_topbar.md +3 -0
- package/web-application/docs/audio-viewer.md +133 -0
- package/web-application/docs/ebook-reader.md +90 -0
- package/web-application/docs/image-viewer.md +90 -0
- package/web-application/docs/server-setup.md +262 -0
- package/web-application/docs/video-viewer.md +134 -0
- package/web-application/docs.html +59 -0
- package/web-application/index.html +1 -1
- package/web-application/js/pict-docuserve.min.js +58 -0
- package/web-application/js/pict.min.js +2 -2
- package/web-application/js/pict.min.js.map +1 -1
- package/web-application/retold-remote.js +2652 -970
- package/web-application/retold-remote.js.map +1 -1
- package/web-application/retold-remote.min.js +60 -13
- package/web-application/retold-remote.min.js.map +1 -1
- package/server.js +0 -43
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
const libPictProvider = require('pict-provider');
|
|
2
2
|
|
|
3
|
+
const libHandleGalleryKey = require('./keyboard-handlers/KeyHandler-Gallery.js');
|
|
4
|
+
const libHandleViewerKey = require('./keyboard-handlers/KeyHandler-Viewer.js');
|
|
5
|
+
const libHandleSidebarKey = require('./keyboard-handlers/KeyHandler-Sidebar.js');
|
|
6
|
+
const libHandleVideoExplorerKey = require('./keyboard-handlers/KeyHandler-VideoExplorer.js');
|
|
7
|
+
const libHandleAudioExplorerKey = require('./keyboard-handlers/KeyHandler-AudioExplorer.js');
|
|
8
|
+
|
|
3
9
|
const _DefaultProviderConfiguration =
|
|
4
10
|
{
|
|
5
11
|
ProviderIdentifier: 'RetoldRemote-GalleryNavigation',
|
|
@@ -171,129 +177,93 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
171
177
|
|
|
172
178
|
document.addEventListener('keydown', this._keydownHandler);
|
|
173
179
|
this._keydownBound = true;
|
|
180
|
+
|
|
181
|
+
// Set up edge-swipe gestures for distraction-free mode on touch devices
|
|
182
|
+
this._setupDFSwipeGestures();
|
|
174
183
|
}
|
|
175
184
|
|
|
176
185
|
/**
|
|
177
|
-
*
|
|
186
|
+
* Set up touch swipe gestures near the top edge for toggling
|
|
187
|
+
* distraction-free mode.
|
|
188
|
+
*
|
|
189
|
+
* - Swipe UP starting within the top 60px of the viewport → enter DF
|
|
190
|
+
* - Swipe DOWN starting within the top 40px of the viewport → exit DF
|
|
191
|
+
*
|
|
192
|
+
* Only single-finger vertical swipes that exceed the threshold are
|
|
193
|
+
* recognised. Horizontal movement greater than vertical is ignored.
|
|
178
194
|
*/
|
|
179
|
-
|
|
195
|
+
_setupDFSwipeGestures()
|
|
180
196
|
{
|
|
181
|
-
|
|
182
|
-
let tmpItems = tmpRemote.GalleryItems || [];
|
|
183
|
-
let tmpIndex = tmpRemote.GalleryCursorIndex || 0;
|
|
184
|
-
|
|
185
|
-
switch (pEvent.key)
|
|
197
|
+
if (this._dfSwipeBound)
|
|
186
198
|
{
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
this.moveCursor(Math.min(tmpIndex + 1, tmpItems.length - 1));
|
|
190
|
-
break;
|
|
191
|
-
|
|
192
|
-
case 'ArrowLeft':
|
|
193
|
-
pEvent.preventDefault();
|
|
194
|
-
this.moveCursor(Math.max(tmpIndex - 1, 0));
|
|
195
|
-
break;
|
|
196
|
-
|
|
197
|
-
case 'ArrowDown':
|
|
198
|
-
pEvent.preventDefault();
|
|
199
|
-
this.moveCursor(Math.min(tmpIndex + this._columnsPerRow, tmpItems.length - 1));
|
|
200
|
-
break;
|
|
201
|
-
|
|
202
|
-
case 'ArrowUp':
|
|
203
|
-
pEvent.preventDefault();
|
|
204
|
-
this.moveCursor(Math.max(tmpIndex - this._columnsPerRow, 0));
|
|
205
|
-
break;
|
|
206
|
-
|
|
207
|
-
case 'Enter':
|
|
208
|
-
pEvent.preventDefault();
|
|
209
|
-
this.openCurrent();
|
|
210
|
-
break;
|
|
211
|
-
|
|
212
|
-
case 'Escape':
|
|
213
|
-
pEvent.preventDefault();
|
|
214
|
-
this.navigateUp();
|
|
215
|
-
break;
|
|
216
|
-
|
|
217
|
-
case 'g':
|
|
218
|
-
pEvent.preventDefault();
|
|
219
|
-
this._toggleViewMode();
|
|
220
|
-
break;
|
|
221
|
-
|
|
222
|
-
case 'x':
|
|
223
|
-
pEvent.preventDefault();
|
|
224
|
-
this._clearAllFilters();
|
|
225
|
-
break;
|
|
226
|
-
|
|
227
|
-
case 'Home':
|
|
228
|
-
pEvent.preventDefault();
|
|
229
|
-
this.moveCursor(0);
|
|
230
|
-
break;
|
|
231
|
-
|
|
232
|
-
case 'End':
|
|
233
|
-
pEvent.preventDefault();
|
|
234
|
-
this.moveCursor(tmpItems.length - 1);
|
|
235
|
-
break;
|
|
236
|
-
|
|
237
|
-
case '1':
|
|
238
|
-
pEvent.preventDefault();
|
|
239
|
-
this.openCurrentAs('image');
|
|
240
|
-
break;
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
241
201
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
202
|
+
let tmpSelf = this;
|
|
203
|
+
let tmpSwipeThreshold = 60;
|
|
204
|
+
let tmpEdgeEnter = 60;
|
|
205
|
+
let tmpEdgeExit = 40;
|
|
206
|
+
let tmpStartY = 0;
|
|
207
|
+
let tmpStartX = 0;
|
|
208
|
+
let tmpStartClientY = 0;
|
|
209
|
+
let tmpTouchCount = 0;
|
|
210
|
+
|
|
211
|
+
this._dfSwipeTouchStart = function (pEvent)
|
|
212
|
+
{
|
|
213
|
+
tmpTouchCount = pEvent.touches.length;
|
|
214
|
+
if (tmpTouchCount !== 1)
|
|
215
|
+
{
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
tmpStartX = pEvent.touches[0].clientX;
|
|
219
|
+
tmpStartY = pEvent.touches[0].clientY;
|
|
220
|
+
tmpStartClientY = tmpStartY;
|
|
221
|
+
};
|
|
246
222
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
223
|
+
this._dfSwipeTouchEnd = function (pEvent)
|
|
224
|
+
{
|
|
225
|
+
if (tmpTouchCount !== 1)
|
|
226
|
+
{
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
251
229
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
230
|
+
let tmpEndX = pEvent.changedTouches[0].clientX;
|
|
231
|
+
let tmpEndY = pEvent.changedTouches[0].clientY;
|
|
232
|
+
let tmpDeltaX = tmpEndX - tmpStartX;
|
|
233
|
+
let tmpDeltaY = tmpEndY - tmpStartY;
|
|
256
234
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
let tmpGalleryView = this.pict.views['RetoldRemote-Gallery'];
|
|
263
|
-
if (tmpGalleryView)
|
|
264
|
-
{
|
|
265
|
-
tmpGalleryView.toggleFilterPanel();
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
break;
|
|
235
|
+
// Must be primarily vertical
|
|
236
|
+
if (Math.abs(tmpDeltaY) < tmpSwipeThreshold || Math.abs(tmpDeltaX) > Math.abs(tmpDeltaY))
|
|
237
|
+
{
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
269
240
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
{
|
|
273
|
-
// Ensure the filter bar is visible first
|
|
274
|
-
this._showFilterBar();
|
|
275
|
-
setTimeout(() =>
|
|
276
|
-
{
|
|
277
|
-
let tmpSortSelect = document.getElementById('RetoldRemote-Gallery-Sort');
|
|
278
|
-
if (tmpSortSelect)
|
|
279
|
-
{
|
|
280
|
-
tmpSortSelect.focus();
|
|
281
|
-
}
|
|
282
|
-
}, 50);
|
|
283
|
-
}
|
|
284
|
-
break;
|
|
241
|
+
let tmpRemote = tmpSelf.pict.AppData.RetoldRemote;
|
|
242
|
+
let tmpIsDF = tmpRemote._distractionFreeMode || false;
|
|
285
243
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
244
|
+
if (!tmpIsDF && tmpDeltaY < 0 && tmpStartClientY <= tmpEdgeEnter)
|
|
245
|
+
{
|
|
246
|
+
// Swipe up from top edge → enter DF
|
|
247
|
+
tmpSelf._toggleDistractionFree();
|
|
248
|
+
}
|
|
249
|
+
else if (tmpIsDF && tmpDeltaY > 0 && tmpStartClientY <= tmpEdgeExit)
|
|
250
|
+
{
|
|
251
|
+
// Swipe down from top edge → exit DF
|
|
252
|
+
tmpSelf._toggleDistractionFree();
|
|
253
|
+
}
|
|
254
|
+
};
|
|
290
255
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
256
|
+
document.addEventListener('touchstart', this._dfSwipeTouchStart, { passive: true });
|
|
257
|
+
document.addEventListener('touchend', this._dfSwipeTouchEnd, { passive: true });
|
|
258
|
+
this._dfSwipeBound = true;
|
|
259
|
+
}
|
|
295
260
|
|
|
296
|
-
|
|
261
|
+
/**
|
|
262
|
+
* Handle keyboard events in gallery mode.
|
|
263
|
+
*/
|
|
264
|
+
_handleGalleryKey(pEvent)
|
|
265
|
+
{
|
|
266
|
+
libHandleGalleryKey(this, pEvent);
|
|
297
267
|
}
|
|
298
268
|
|
|
299
269
|
/**
|
|
@@ -301,60 +271,7 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
301
271
|
*/
|
|
302
272
|
_handleSidebarKey(pEvent)
|
|
303
273
|
{
|
|
304
|
-
|
|
305
|
-
let tmpCount = tmpRows.length;
|
|
306
|
-
|
|
307
|
-
if (tmpCount === 0)
|
|
308
|
-
{
|
|
309
|
-
// Nothing in the sidebar, bail back to gallery
|
|
310
|
-
this._blurSidebar();
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
switch (pEvent.key)
|
|
315
|
-
{
|
|
316
|
-
case 'ArrowDown':
|
|
317
|
-
pEvent.preventDefault();
|
|
318
|
-
this._moveSidebarCursor(Math.min(this._sidebarCursorIndex + 1, tmpCount - 1));
|
|
319
|
-
break;
|
|
320
|
-
|
|
321
|
-
case 'ArrowUp':
|
|
322
|
-
pEvent.preventDefault();
|
|
323
|
-
this._moveSidebarCursor(Math.max(this._sidebarCursorIndex - 1, 0));
|
|
324
|
-
break;
|
|
325
|
-
|
|
326
|
-
case 'Home':
|
|
327
|
-
pEvent.preventDefault();
|
|
328
|
-
this._moveSidebarCursor(0);
|
|
329
|
-
break;
|
|
330
|
-
|
|
331
|
-
case 'End':
|
|
332
|
-
pEvent.preventDefault();
|
|
333
|
-
this._moveSidebarCursor(tmpCount - 1);
|
|
334
|
-
break;
|
|
335
|
-
|
|
336
|
-
case 'Enter':
|
|
337
|
-
pEvent.preventDefault();
|
|
338
|
-
{
|
|
339
|
-
// Click the focused row to open it (folder or file)
|
|
340
|
-
let tmpRow = tmpRows[this._sidebarCursorIndex];
|
|
341
|
-
if (tmpRow)
|
|
342
|
-
{
|
|
343
|
-
// Fire the dblclick handler which opens folders / selects files
|
|
344
|
-
let tmpDblClickHandler = tmpRow.getAttribute('ondblclick');
|
|
345
|
-
if (tmpDblClickHandler)
|
|
346
|
-
{
|
|
347
|
-
new Function(tmpDblClickHandler).call(tmpRow);
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
break;
|
|
352
|
-
|
|
353
|
-
case 'Escape':
|
|
354
|
-
pEvent.preventDefault();
|
|
355
|
-
this._blurSidebar();
|
|
356
|
-
break;
|
|
357
|
-
}
|
|
274
|
+
libHandleSidebarKey(this, pEvent);
|
|
358
275
|
}
|
|
359
276
|
|
|
360
277
|
/**
|
|
@@ -435,156 +352,7 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
435
352
|
*/
|
|
436
353
|
_handleViewerKey(pEvent)
|
|
437
354
|
{
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
// Video action menu mode — intercept keys for menu options
|
|
441
|
-
if (tmpRemote.VideoMenuActive && tmpRemote.CurrentViewerMediaType === 'video')
|
|
442
|
-
{
|
|
443
|
-
switch (pEvent.key)
|
|
444
|
-
{
|
|
445
|
-
case 'Escape':
|
|
446
|
-
pEvent.preventDefault();
|
|
447
|
-
this.closeViewer();
|
|
448
|
-
return;
|
|
449
|
-
|
|
450
|
-
case 'ArrowRight':
|
|
451
|
-
case 'j':
|
|
452
|
-
pEvent.preventDefault();
|
|
453
|
-
this.nextFile();
|
|
454
|
-
return;
|
|
455
|
-
|
|
456
|
-
case 'ArrowLeft':
|
|
457
|
-
case 'k':
|
|
458
|
-
pEvent.preventDefault();
|
|
459
|
-
this.prevFile();
|
|
460
|
-
return;
|
|
461
|
-
|
|
462
|
-
case 'e':
|
|
463
|
-
pEvent.preventDefault();
|
|
464
|
-
let tmpVEX = this.pict.views['RetoldRemote-VideoExplorer'];
|
|
465
|
-
if (tmpVEX)
|
|
466
|
-
{
|
|
467
|
-
tmpVEX.showExplorer(tmpRemote.CurrentViewerFile);
|
|
468
|
-
}
|
|
469
|
-
return;
|
|
470
|
-
|
|
471
|
-
case ' ':
|
|
472
|
-
case 'Enter':
|
|
473
|
-
pEvent.preventDefault();
|
|
474
|
-
let tmpViewer = this.pict.views['RetoldRemote-MediaViewer'];
|
|
475
|
-
if (tmpViewer)
|
|
476
|
-
{
|
|
477
|
-
tmpViewer.playVideo();
|
|
478
|
-
}
|
|
479
|
-
return;
|
|
480
|
-
|
|
481
|
-
case 't':
|
|
482
|
-
pEvent.preventDefault();
|
|
483
|
-
let tmpMediaViewer = this.pict.views['RetoldRemote-MediaViewer'];
|
|
484
|
-
if (tmpMediaViewer)
|
|
485
|
-
{
|
|
486
|
-
tmpMediaViewer.loadVideoMenuFrame();
|
|
487
|
-
}
|
|
488
|
-
return;
|
|
489
|
-
|
|
490
|
-
case 'v':
|
|
491
|
-
pEvent.preventDefault();
|
|
492
|
-
this._streamWithVLC();
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
switch (pEvent.key)
|
|
499
|
-
{
|
|
500
|
-
case 'Escape':
|
|
501
|
-
pEvent.preventDefault();
|
|
502
|
-
this.closeViewer();
|
|
503
|
-
break;
|
|
504
|
-
|
|
505
|
-
case 'ArrowRight':
|
|
506
|
-
case 'j':
|
|
507
|
-
pEvent.preventDefault();
|
|
508
|
-
this.nextFile();
|
|
509
|
-
break;
|
|
510
|
-
|
|
511
|
-
case 'ArrowLeft':
|
|
512
|
-
case 'k':
|
|
513
|
-
pEvent.preventDefault();
|
|
514
|
-
this.prevFile();
|
|
515
|
-
break;
|
|
516
|
-
|
|
517
|
-
case 'f':
|
|
518
|
-
pEvent.preventDefault();
|
|
519
|
-
this._toggleFullscreen();
|
|
520
|
-
break;
|
|
521
|
-
|
|
522
|
-
case 'i':
|
|
523
|
-
pEvent.preventDefault();
|
|
524
|
-
this._toggleFileInfo();
|
|
525
|
-
break;
|
|
526
|
-
|
|
527
|
-
case ' ':
|
|
528
|
-
pEvent.preventDefault();
|
|
529
|
-
this._togglePlayPause();
|
|
530
|
-
break;
|
|
531
|
-
|
|
532
|
-
case '+':
|
|
533
|
-
case '=':
|
|
534
|
-
pEvent.preventDefault();
|
|
535
|
-
this._zoomIn();
|
|
536
|
-
break;
|
|
537
|
-
|
|
538
|
-
case '-':
|
|
539
|
-
pEvent.preventDefault();
|
|
540
|
-
this._zoomOut();
|
|
541
|
-
break;
|
|
542
|
-
|
|
543
|
-
case '0':
|
|
544
|
-
pEvent.preventDefault();
|
|
545
|
-
this._zoomReset();
|
|
546
|
-
break;
|
|
547
|
-
|
|
548
|
-
case 'z':
|
|
549
|
-
pEvent.preventDefault();
|
|
550
|
-
this._cycleFitMode();
|
|
551
|
-
break;
|
|
552
|
-
|
|
553
|
-
case 'Enter':
|
|
554
|
-
pEvent.preventDefault();
|
|
555
|
-
this._streamWithVLC();
|
|
556
|
-
break;
|
|
557
|
-
|
|
558
|
-
case 'v':
|
|
559
|
-
pEvent.preventDefault();
|
|
560
|
-
this._streamWithVLC();
|
|
561
|
-
break;
|
|
562
|
-
|
|
563
|
-
case 'd':
|
|
564
|
-
pEvent.preventDefault();
|
|
565
|
-
this._toggleDistractionFree();
|
|
566
|
-
break;
|
|
567
|
-
|
|
568
|
-
case '1':
|
|
569
|
-
pEvent.preventDefault();
|
|
570
|
-
this.switchViewerType('image');
|
|
571
|
-
break;
|
|
572
|
-
|
|
573
|
-
case '2':
|
|
574
|
-
pEvent.preventDefault();
|
|
575
|
-
this.switchViewerType('video');
|
|
576
|
-
break;
|
|
577
|
-
|
|
578
|
-
case '3':
|
|
579
|
-
pEvent.preventDefault();
|
|
580
|
-
this.switchViewerType('audio');
|
|
581
|
-
break;
|
|
582
|
-
|
|
583
|
-
case '4':
|
|
584
|
-
pEvent.preventDefault();
|
|
585
|
-
this.switchViewerType('text');
|
|
586
|
-
break;
|
|
587
|
-
}
|
|
355
|
+
libHandleViewerKey(this, pEvent);
|
|
588
356
|
}
|
|
589
357
|
|
|
590
358
|
/**
|
|
@@ -592,17 +360,7 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
592
360
|
*/
|
|
593
361
|
_handleVideoExplorerKey(pEvent)
|
|
594
362
|
{
|
|
595
|
-
|
|
596
|
-
{
|
|
597
|
-
case 'Escape':
|
|
598
|
-
pEvent.preventDefault();
|
|
599
|
-
let tmpVEX = this.pict.views['RetoldRemote-VideoExplorer'];
|
|
600
|
-
if (tmpVEX)
|
|
601
|
-
{
|
|
602
|
-
tmpVEX.goBack();
|
|
603
|
-
}
|
|
604
|
-
break;
|
|
605
|
-
}
|
|
363
|
+
libHandleVideoExplorerKey(this, pEvent);
|
|
606
364
|
}
|
|
607
365
|
|
|
608
366
|
/**
|
|
@@ -610,49 +368,7 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
610
368
|
*/
|
|
611
369
|
_handleAudioExplorerKey(pEvent)
|
|
612
370
|
{
|
|
613
|
-
|
|
614
|
-
if (!tmpAEX)
|
|
615
|
-
{
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
switch (pEvent.key)
|
|
620
|
-
{
|
|
621
|
-
case 'Escape':
|
|
622
|
-
pEvent.preventDefault();
|
|
623
|
-
if (tmpAEX._selectionStart >= 0)
|
|
624
|
-
{
|
|
625
|
-
tmpAEX.clearSelection();
|
|
626
|
-
}
|
|
627
|
-
else
|
|
628
|
-
{
|
|
629
|
-
tmpAEX.goBack();
|
|
630
|
-
}
|
|
631
|
-
break;
|
|
632
|
-
case '+':
|
|
633
|
-
case '=':
|
|
634
|
-
pEvent.preventDefault();
|
|
635
|
-
tmpAEX.zoomIn();
|
|
636
|
-
break;
|
|
637
|
-
case '-':
|
|
638
|
-
case '_':
|
|
639
|
-
pEvent.preventDefault();
|
|
640
|
-
tmpAEX.zoomOut();
|
|
641
|
-
break;
|
|
642
|
-
case '0':
|
|
643
|
-
pEvent.preventDefault();
|
|
644
|
-
tmpAEX.zoomToFit();
|
|
645
|
-
break;
|
|
646
|
-
case 'z':
|
|
647
|
-
case 'Z':
|
|
648
|
-
pEvent.preventDefault();
|
|
649
|
-
tmpAEX.zoomToSelection();
|
|
650
|
-
break;
|
|
651
|
-
case ' ':
|
|
652
|
-
pEvent.preventDefault();
|
|
653
|
-
tmpAEX.playSelection();
|
|
654
|
-
break;
|
|
655
|
-
}
|
|
371
|
+
libHandleAudioExplorerKey(this, pEvent);
|
|
656
372
|
}
|
|
657
373
|
|
|
658
374
|
/**
|
|
@@ -686,6 +402,13 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
686
402
|
// Scroll the tile into view if needed
|
|
687
403
|
tmpNewTile.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
688
404
|
}
|
|
405
|
+
|
|
406
|
+
// Update the top bar info to reflect the new cursor position
|
|
407
|
+
let tmpTopBar = this.pict.views['ContentEditor-TopBar'];
|
|
408
|
+
if (tmpTopBar)
|
|
409
|
+
{
|
|
410
|
+
tmpTopBar.updateInfo();
|
|
411
|
+
}
|
|
689
412
|
}
|
|
690
413
|
|
|
691
414
|
/**
|
|
@@ -815,12 +538,30 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
815
538
|
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
816
539
|
tmpRemote.ActiveMode = 'gallery';
|
|
817
540
|
|
|
541
|
+
// Exit collection browsing mode
|
|
542
|
+
tmpRemote.BrowsingCollection = false;
|
|
543
|
+
tmpRemote.BrowsingCollectionIndex = -1;
|
|
544
|
+
|
|
818
545
|
let tmpGalleryContainer = document.getElementById('RetoldRemote-Gallery-Container');
|
|
819
546
|
let tmpViewerContainer = document.getElementById('RetoldRemote-Viewer-Container');
|
|
820
547
|
|
|
821
548
|
if (tmpGalleryContainer) tmpGalleryContainer.style.display = '';
|
|
822
549
|
if (tmpViewerContainer) tmpViewerContainer.style.display = 'none';
|
|
823
550
|
|
|
551
|
+
// Clean up swipe and DF exit listeners
|
|
552
|
+
let tmpMediaViewer = this.pict.views['RetoldRemote-MediaViewer'];
|
|
553
|
+
if (tmpMediaViewer)
|
|
554
|
+
{
|
|
555
|
+
if (tmpMediaViewer._cleanupSwipe)
|
|
556
|
+
{
|
|
557
|
+
tmpMediaViewer._cleanupSwipe();
|
|
558
|
+
}
|
|
559
|
+
if (tmpMediaViewer._cleanupDFExitHotspot)
|
|
560
|
+
{
|
|
561
|
+
tmpMediaViewer._cleanupDFExitHotspot();
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
824
565
|
// Restore the hash to the browse route (use hashed identifier when available)
|
|
825
566
|
let tmpCurrentLocation = (this.pict.AppData.PictFileBrowser && this.pict.AppData.PictFileBrowser.CurrentLocation) || '';
|
|
826
567
|
let tmpFragProvider = this.pict.providers['RetoldRemote-Provider'];
|
|
@@ -837,10 +578,37 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
837
578
|
|
|
838
579
|
/**
|
|
839
580
|
* Navigate to the next file in the gallery list.
|
|
581
|
+
* When browsing a collection, navigates through collection items instead.
|
|
840
582
|
*/
|
|
841
583
|
nextFile()
|
|
842
584
|
{
|
|
843
585
|
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
586
|
+
|
|
587
|
+
// Collection browsing mode — navigate through ActiveCollection.Items
|
|
588
|
+
if (tmpRemote.BrowsingCollection && tmpRemote.ActiveCollection)
|
|
589
|
+
{
|
|
590
|
+
let tmpCollItems = tmpRemote.ActiveCollection.Items || [];
|
|
591
|
+
let tmpCollIndex = tmpRemote.BrowsingCollectionIndex;
|
|
592
|
+
|
|
593
|
+
for (let i = tmpCollIndex + 1; i < tmpCollItems.length; i++)
|
|
594
|
+
{
|
|
595
|
+
let tmpItem = tmpCollItems[i];
|
|
596
|
+
if (tmpItem.Type === 'file' || tmpItem.Type === 'subfile' ||
|
|
597
|
+
tmpItem.Type === 'image-crop' || tmpItem.Type === 'video-clip' ||
|
|
598
|
+
tmpItem.Type === 'video-frame')
|
|
599
|
+
{
|
|
600
|
+
tmpRemote.BrowsingCollectionIndex = i;
|
|
601
|
+
let tmpApp = this.pict.PictApplication;
|
|
602
|
+
if (tmpApp && tmpApp.navigateToFile)
|
|
603
|
+
{
|
|
604
|
+
tmpApp.navigateToFile(tmpItem.Path);
|
|
605
|
+
}
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
|
|
844
612
|
let tmpItems = tmpRemote.GalleryItems || [];
|
|
845
613
|
let tmpIndex = tmpRemote.GalleryCursorIndex || 0;
|
|
846
614
|
|
|
@@ -862,10 +630,37 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
862
630
|
|
|
863
631
|
/**
|
|
864
632
|
* Navigate to the previous file in the gallery list.
|
|
633
|
+
* When browsing a collection, navigates through collection items instead.
|
|
865
634
|
*/
|
|
866
635
|
prevFile()
|
|
867
636
|
{
|
|
868
637
|
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
638
|
+
|
|
639
|
+
// Collection browsing mode — navigate through ActiveCollection.Items
|
|
640
|
+
if (tmpRemote.BrowsingCollection && tmpRemote.ActiveCollection)
|
|
641
|
+
{
|
|
642
|
+
let tmpCollItems = tmpRemote.ActiveCollection.Items || [];
|
|
643
|
+
let tmpCollIndex = tmpRemote.BrowsingCollectionIndex;
|
|
644
|
+
|
|
645
|
+
for (let i = tmpCollIndex - 1; i >= 0; i--)
|
|
646
|
+
{
|
|
647
|
+
let tmpItem = tmpCollItems[i];
|
|
648
|
+
if (tmpItem.Type === 'file' || tmpItem.Type === 'subfile' ||
|
|
649
|
+
tmpItem.Type === 'image-crop' || tmpItem.Type === 'video-clip' ||
|
|
650
|
+
tmpItem.Type === 'video-frame')
|
|
651
|
+
{
|
|
652
|
+
tmpRemote.BrowsingCollectionIndex = i;
|
|
653
|
+
let tmpApp = this.pict.PictApplication;
|
|
654
|
+
if (tmpApp && tmpApp.navigateToFile)
|
|
655
|
+
{
|
|
656
|
+
tmpApp.navigateToFile(tmpItem.Path);
|
|
657
|
+
}
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
|
|
869
664
|
let tmpItems = tmpRemote.GalleryItems || [];
|
|
870
665
|
let tmpIndex = tmpRemote.GalleryCursorIndex || 0;
|
|
871
666
|
|
|
@@ -980,6 +775,7 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
980
775
|
{
|
|
981
776
|
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
982
777
|
tmpRemote.FilterBarVisible = false;
|
|
778
|
+
tmpRemote.FilterPanelOpen = false;
|
|
983
779
|
|
|
984
780
|
let tmpGalleryView = this.pict.views['RetoldRemote-Gallery'];
|
|
985
781
|
if (tmpGalleryView)
|
|
@@ -999,7 +795,7 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
999
795
|
tmpGalleryView.clearAllFilters();
|
|
1000
796
|
}
|
|
1001
797
|
|
|
1002
|
-
this.
|
|
798
|
+
this.pict.providers['RetoldRemote-ToastNotification'].showOverlayIndicator('Filters cleared');
|
|
1003
799
|
}
|
|
1004
800
|
|
|
1005
801
|
// ──────────────────────────────────────────────
|
|
@@ -1065,7 +861,8 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
1065
861
|
['s', 'Focus sort dropdown'],
|
|
1066
862
|
['x', 'Clear all filters'],
|
|
1067
863
|
['c', 'Settings / config panel'],
|
|
1068
|
-
['d', 'Distraction-free mode']
|
|
864
|
+
['d', 'Distraction-free mode'],
|
|
865
|
+
['e', 'Video explorer (on video files)']
|
|
1069
866
|
];
|
|
1070
867
|
for (let i = 0; i < tmpGalleryShortcuts.length; i++)
|
|
1071
868
|
{
|
|
@@ -1142,6 +939,14 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
1142
939
|
}
|
|
1143
940
|
tmpHTML += '</div>';
|
|
1144
941
|
|
|
942
|
+
// Documentation link
|
|
943
|
+
tmpHTML += '<div class="retold-remote-help-section">';
|
|
944
|
+
tmpHTML += '<div class="retold-remote-help-row" style="justify-content: center; padding: 8px 0;">';
|
|
945
|
+
tmpHTML += '<a href="docs.html" target="_blank" style="color: var(--retold-accent, #569cd6); text-decoration: none; font-size: 0.9rem; cursor: pointer;">';
|
|
946
|
+
tmpHTML += 'View Documentation ↗</a>';
|
|
947
|
+
tmpHTML += '</div>';
|
|
948
|
+
tmpHTML += '</div>';
|
|
949
|
+
|
|
1145
950
|
// Active mode indicator
|
|
1146
951
|
tmpHTML += '<div class="retold-remote-help-footer">';
|
|
1147
952
|
let tmpModeLabel = 'Gallery';
|
|
@@ -1293,6 +1098,13 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
1293
1098
|
}
|
|
1294
1099
|
}
|
|
1295
1100
|
|
|
1101
|
+
// Sync DF toggle/hotspot in the media viewer
|
|
1102
|
+
let tmpMediaViewer = this.pict.views['RetoldRemote-MediaViewer'];
|
|
1103
|
+
if (tmpMediaViewer && tmpMediaViewer._updateDFControls)
|
|
1104
|
+
{
|
|
1105
|
+
tmpMediaViewer._updateDFControls();
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1296
1108
|
// Recalculate gallery columns after layout change
|
|
1297
1109
|
setTimeout(() => this.recalculateColumns(), 100);
|
|
1298
1110
|
}
|
|
@@ -1330,7 +1142,8 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
1330
1142
|
let tmpInfoOverlay = document.getElementById('RetoldRemote-FileInfo-Overlay');
|
|
1331
1143
|
if (tmpInfoOverlay)
|
|
1332
1144
|
{
|
|
1333
|
-
|
|
1145
|
+
let tmpIsHidden = window.getComputedStyle(tmpInfoOverlay).display === 'none';
|
|
1146
|
+
tmpInfoOverlay.style.display = tmpIsHidden ? 'block' : 'none';
|
|
1334
1147
|
}
|
|
1335
1148
|
}
|
|
1336
1149
|
|
|
@@ -1416,7 +1229,7 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
1416
1229
|
}
|
|
1417
1230
|
|
|
1418
1231
|
// Show a brief toast
|
|
1419
|
-
this.
|
|
1232
|
+
this.pict.providers['RetoldRemote-ToastNotification'].showOverlayIndicator('Opening in VLC...');
|
|
1420
1233
|
|
|
1421
1234
|
// POST to the server to open the file
|
|
1422
1235
|
fetch('/api/media/open',
|
|
@@ -1433,12 +1246,12 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
1433
1246
|
{
|
|
1434
1247
|
if (!pData.Success)
|
|
1435
1248
|
{
|
|
1436
|
-
this.
|
|
1249
|
+
this.pict.providers['RetoldRemote-ToastNotification'].showOverlayIndicator('Failed to open: ' + (pData.Error || 'Unknown error'));
|
|
1437
1250
|
}
|
|
1438
1251
|
})
|
|
1439
1252
|
.catch((pError) =>
|
|
1440
1253
|
{
|
|
1441
|
-
this.
|
|
1254
|
+
this.pict.providers['RetoldRemote-ToastNotification'].showOverlayIndicator('Failed to open: ' + pError.message);
|
|
1442
1255
|
});
|
|
1443
1256
|
}
|
|
1444
1257
|
|
|
@@ -1471,7 +1284,7 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
1471
1284
|
? ('vlc://' + tmpStreamURL)
|
|
1472
1285
|
: ('vlc://' + encodeURIComponent(tmpStreamURL));
|
|
1473
1286
|
|
|
1474
|
-
this.
|
|
1287
|
+
this.pict.providers['RetoldRemote-ToastNotification'].showOverlayIndicator('Opening VLC...');
|
|
1475
1288
|
|
|
1476
1289
|
// Use a temporary anchor element to trigger the protocol handler
|
|
1477
1290
|
// without navigating the current page away
|
|
@@ -1483,41 +1296,6 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
1483
1296
|
document.body.removeChild(tmpLink);
|
|
1484
1297
|
}
|
|
1485
1298
|
|
|
1486
|
-
/**
|
|
1487
|
-
* Show a brief toast notification in the viewer.
|
|
1488
|
-
*
|
|
1489
|
-
* @param {string} pMessage - Text to display
|
|
1490
|
-
*/
|
|
1491
|
-
_showToast(pMessage)
|
|
1492
|
-
{
|
|
1493
|
-
let tmpIndicator = document.getElementById('RetoldRemote-FitIndicator');
|
|
1494
|
-
if (!tmpIndicator)
|
|
1495
|
-
{
|
|
1496
|
-
tmpIndicator = document.createElement('div');
|
|
1497
|
-
tmpIndicator.id = 'RetoldRemote-FitIndicator';
|
|
1498
|
-
tmpIndicator.className = 'retold-remote-fit-indicator';
|
|
1499
|
-
|
|
1500
|
-
let tmpContainer = document.querySelector('.retold-remote-viewer-body');
|
|
1501
|
-
if (tmpContainer)
|
|
1502
|
-
{
|
|
1503
|
-
tmpContainer.appendChild(tmpIndicator);
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
|
|
1507
|
-
tmpIndicator.textContent = pMessage;
|
|
1508
|
-
tmpIndicator.classList.add('visible');
|
|
1509
|
-
|
|
1510
|
-
if (this._toastTimeout)
|
|
1511
|
-
{
|
|
1512
|
-
clearTimeout(this._toastTimeout);
|
|
1513
|
-
}
|
|
1514
|
-
|
|
1515
|
-
let tmpSelf = this;
|
|
1516
|
-
this._toastTimeout = setTimeout(function ()
|
|
1517
|
-
{
|
|
1518
|
-
tmpIndicator.classList.remove('visible');
|
|
1519
|
-
}, 1500);
|
|
1520
|
-
}
|
|
1521
1299
|
}
|
|
1522
1300
|
|
|
1523
1301
|
GalleryNavigationProvider.default_configuration = _DefaultProviderConfiguration;
|