retold-remote 0.0.22 → 0.0.25
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 +87 -20
- package/docs/README.md +59 -11
- package/docs/_sidebar.md +1 -0
- package/docs/collections.md +30 -0
- package/docs/ebook-reader.md +75 -1
- package/docs/image-explorer.md +27 -1
- package/docs/server-setup.md +28 -18
- package/docs/stack-launcher.md +218 -0
- package/docs/ultravisor-integration.md +2 -0
- package/package.json +10 -7
- package/source/Pict-Application-RetoldRemote.js +2 -0
- package/source/RetoldRemote-ExtensionMaps.js +1 -1
- package/source/cli/RetoldRemote-Server-Setup.js +240 -2
- package/source/cli/RetoldRemote-Stack-Launcher.js +387 -0
- package/source/cli/RetoldRemote-Stack-Run.js +41 -0
- package/source/cli/commands/RetoldRemote-Command-Serve.js +129 -54
- package/source/providers/CollectionManager-AddItems.js +166 -0
- package/source/providers/Pict-Provider-GalleryNavigation.js +46 -0
- package/source/providers/keyboard-handlers/KeyHandler-ImageExplorer.js +5 -0
- package/source/providers/keyboard-handlers/KeyHandler-Viewer.js +23 -0
- package/source/server/RetoldRemote-CollectionExportService.js +696 -0
- package/source/server/RetoldRemote-CollectionService.js +5 -0
- package/source/server/RetoldRemote-EbookService.js +194 -3
- package/source/server/RetoldRemote-SubimageService.js +530 -0
- package/source/server/RetoldRemote-ToolDetector.js +50 -0
- package/source/server/RetoldRemote-UltravisorOperations.js +6 -6
- package/source/views/MediaViewer-EbookViewer.js +419 -1
- package/source/views/MediaViewer-PdfViewer.js +963 -0
- package/source/views/PictView-Remote-CollectionsPanel.js +166 -0
- package/source/views/PictView-Remote-ImageExplorer.js +606 -1
- package/source/views/PictView-Remote-ImageViewer.js +2 -2
- package/source/views/PictView-Remote-Layout.js +12 -0
- package/source/views/PictView-Remote-MediaViewer.js +83 -25
- package/source/views/PictView-Remote-SubimagesPanel.js +353 -0
- package/web-application/css/retold-remote.css +87 -20
- package/web-application/docs/README.md +59 -11
- package/web-application/docs/_sidebar.md +1 -0
- package/web-application/docs/collections.md +30 -0
- package/web-application/docs/ebook-reader.md +75 -1
- package/web-application/docs/image-explorer.md +27 -1
- package/web-application/docs/server-setup.md +28 -18
- package/web-application/docs/stack-launcher.md +218 -0
- package/web-application/docs/ultravisor-integration.md +2 -0
- package/web-application/retold-remote.js +399 -45
- package/web-application/retold-remote.js.map +1 -1
- package/web-application/retold-remote.min.js +13 -12
- package/web-application/retold-remote.min.js.map +1 -1
|
@@ -47,6 +47,30 @@ module.exports =
|
|
|
47
47
|
return this.addAudioSnippetToCollection(tmpTargetGUID);
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
// If the image explorer is active with a selection, add as subimage
|
|
51
|
+
if (tmpRemote.ActiveMode === 'image-explorer')
|
|
52
|
+
{
|
|
53
|
+
let tmpIEX = this.pict.views['RetoldRemote-ImageExplorer'];
|
|
54
|
+
if (tmpIEX)
|
|
55
|
+
{
|
|
56
|
+
let tmpActiveSelection = tmpIEX.getActiveSelection();
|
|
57
|
+
if (tmpActiveSelection)
|
|
58
|
+
{
|
|
59
|
+
return this.addSubimageToCollection(tmpTargetGUID, tmpActiveSelection, tmpIEX._currentPath);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// If viewing a document (PDF/EPUB) with a pending region, add as document-region
|
|
65
|
+
if (tmpRemote.ActiveMode === 'viewer' && tmpRemote.CurrentViewerMediaType === 'document')
|
|
66
|
+
{
|
|
67
|
+
let tmpMediaViewer = this.pict.views['RetoldRemote-MediaViewer'];
|
|
68
|
+
if (tmpMediaViewer && tmpMediaViewer._pendingDocumentRegion)
|
|
69
|
+
{
|
|
70
|
+
return this.addDocumentRegionToCollection(tmpTargetGUID, tmpMediaViewer._pendingDocumentRegion, tmpRemote.CurrentViewerFile);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
50
74
|
let tmpCurrentItem = this._resolveCurrentItem();
|
|
51
75
|
|
|
52
76
|
if (!tmpCurrentItem || !tmpCurrentItem.Path)
|
|
@@ -329,6 +353,148 @@ module.exports =
|
|
|
329
353
|
return true;
|
|
330
354
|
},
|
|
331
355
|
|
|
356
|
+
/**
|
|
357
|
+
* Add a subimage region (crop) to a collection.
|
|
358
|
+
*
|
|
359
|
+
* @param {string} pGUID - Collection GUID
|
|
360
|
+
* @param {object} pRegion - { X, Y, Width, Height, Label?, ID? } in image pixels
|
|
361
|
+
* @param {string} [pFilePath] - Explicit file path (defaults to current viewer file)
|
|
362
|
+
* @returns {boolean} true if the add was initiated
|
|
363
|
+
*/
|
|
364
|
+
addSubimageToCollection: function addSubimageToCollection(pGUID, pRegion, pFilePath)
|
|
365
|
+
{
|
|
366
|
+
let tmpRemote = this._getRemote();
|
|
367
|
+
let tmpTargetGUID = pGUID || tmpRemote.LastUsedCollectionGUID;
|
|
368
|
+
if (!tmpTargetGUID || !pRegion)
|
|
369
|
+
{
|
|
370
|
+
return false;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
let tmpFilePath = pFilePath || tmpRemote.CurrentViewerFile;
|
|
374
|
+
if (!tmpFilePath)
|
|
375
|
+
{
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
let tmpFileName = tmpFilePath.replace(/^.*\//, '');
|
|
380
|
+
let tmpLabel = pRegion.Label
|
|
381
|
+
? pRegion.Label
|
|
382
|
+
: tmpFileName + ': ' + pRegion.Width + '\u00d7' + pRegion.Height + ' at ' + pRegion.X + ',' + pRegion.Y;
|
|
383
|
+
|
|
384
|
+
let tmpItem =
|
|
385
|
+
{
|
|
386
|
+
Type: 'image-crop',
|
|
387
|
+
Path: tmpFilePath,
|
|
388
|
+
CropRegion:
|
|
389
|
+
{
|
|
390
|
+
X: pRegion.X,
|
|
391
|
+
Y: pRegion.Y,
|
|
392
|
+
Width: pRegion.Width,
|
|
393
|
+
Height: pRegion.Height
|
|
394
|
+
},
|
|
395
|
+
Label: tmpLabel,
|
|
396
|
+
Note: ''
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// If we have a hash for this file, include it
|
|
400
|
+
let tmpProvider = this.pict.providers['RetoldRemote-Provider'];
|
|
401
|
+
if (tmpProvider)
|
|
402
|
+
{
|
|
403
|
+
let tmpHash = tmpProvider.getHashForPath(tmpFilePath);
|
|
404
|
+
if (tmpHash)
|
|
405
|
+
{
|
|
406
|
+
tmpItem.Hash = tmpHash;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
this.addItemsToCollection(tmpTargetGUID, [tmpItem]);
|
|
411
|
+
return true;
|
|
412
|
+
},
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Add a document region (text selection or visual area) to a collection.
|
|
416
|
+
*
|
|
417
|
+
* @param {string} pGUID - Collection GUID
|
|
418
|
+
* @param {object} pRegion - Region object with Type, Label, and type-specific fields
|
|
419
|
+
* @param {string} [pFilePath] - Explicit file path (defaults to current viewer file)
|
|
420
|
+
* @returns {boolean} true if the add was initiated
|
|
421
|
+
*/
|
|
422
|
+
addDocumentRegionToCollection: function addDocumentRegionToCollection(pGUID, pRegion, pFilePath)
|
|
423
|
+
{
|
|
424
|
+
let tmpRemote = this._getRemote();
|
|
425
|
+
let tmpTargetGUID = pGUID || tmpRemote.LastUsedCollectionGUID;
|
|
426
|
+
if (!tmpTargetGUID || !pRegion)
|
|
427
|
+
{
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
let tmpFilePath = pFilePath || tmpRemote.CurrentViewerFile;
|
|
432
|
+
if (!tmpFilePath)
|
|
433
|
+
{
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
let tmpFileName = tmpFilePath.replace(/^.*\//, '');
|
|
438
|
+
let tmpIsText = (pRegion.Type === 'text-selection');
|
|
439
|
+
|
|
440
|
+
// Build label
|
|
441
|
+
let tmpLabel = pRegion.Label || '';
|
|
442
|
+
if (!tmpLabel)
|
|
443
|
+
{
|
|
444
|
+
if (tmpIsText && pRegion.SelectedText)
|
|
445
|
+
{
|
|
446
|
+
tmpLabel = pRegion.SelectedText.substring(0, 50);
|
|
447
|
+
if (pRegion.SelectedText.length > 50) tmpLabel += '\u2026';
|
|
448
|
+
}
|
|
449
|
+
else
|
|
450
|
+
{
|
|
451
|
+
tmpLabel = tmpFileName;
|
|
452
|
+
}
|
|
453
|
+
if (pRegion.PageNumber)
|
|
454
|
+
{
|
|
455
|
+
tmpLabel = 'p.' + pRegion.PageNumber + ': ' + tmpLabel;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
let tmpItem =
|
|
460
|
+
{
|
|
461
|
+
Type: 'document-region',
|
|
462
|
+
Path: tmpFilePath,
|
|
463
|
+
Label: tmpLabel,
|
|
464
|
+
Note: '',
|
|
465
|
+
DocumentRegionType: pRegion.Type || 'visual-region',
|
|
466
|
+
PageNumber: pRegion.PageNumber || null,
|
|
467
|
+
CFI: pRegion.CFI || null,
|
|
468
|
+
SelectedText: pRegion.SelectedText || null
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
// Include crop region for visual selections
|
|
472
|
+
if (!tmpIsText && pRegion.X !== undefined)
|
|
473
|
+
{
|
|
474
|
+
tmpItem.CropRegion =
|
|
475
|
+
{
|
|
476
|
+
X: pRegion.X,
|
|
477
|
+
Y: pRegion.Y,
|
|
478
|
+
Width: pRegion.Width,
|
|
479
|
+
Height: pRegion.Height
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// File hash
|
|
484
|
+
let tmpProvider = this.pict.providers['RetoldRemote-Provider'];
|
|
485
|
+
if (tmpProvider)
|
|
486
|
+
{
|
|
487
|
+
let tmpHash = tmpProvider.getHashForPath(tmpFilePath);
|
|
488
|
+
if (tmpHash)
|
|
489
|
+
{
|
|
490
|
+
tmpItem.Hash = tmpHash;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
this.addItemsToCollection(tmpTargetGUID, [tmpItem]);
|
|
495
|
+
return true;
|
|
496
|
+
},
|
|
497
|
+
|
|
332
498
|
/**
|
|
333
499
|
* Format a timestamp in seconds to a human-readable string.
|
|
334
500
|
*
|
|
@@ -630,6 +630,52 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
630
630
|
tmpAEX.showExplorer(pItem.Path, pItem.AudioStart, pItem.AudioEnd);
|
|
631
631
|
}
|
|
632
632
|
}
|
|
633
|
+
else if (pItem.Type === 'document-region')
|
|
634
|
+
{
|
|
635
|
+
// Navigate to the document, then to the specific location
|
|
636
|
+
let tmpApp = this.pict.PictApplication;
|
|
637
|
+
if (tmpApp && tmpApp.navigateToFile)
|
|
638
|
+
{
|
|
639
|
+
tmpApp.navigateToFile(pItem.Path);
|
|
640
|
+
|
|
641
|
+
// After the viewer loads, navigate to the specific page/CFI
|
|
642
|
+
let tmpSelf = this;
|
|
643
|
+
setTimeout(() =>
|
|
644
|
+
{
|
|
645
|
+
let tmpMediaViewer = tmpSelf.pict.views['RetoldRemote-MediaViewer'];
|
|
646
|
+
if (tmpMediaViewer)
|
|
647
|
+
{
|
|
648
|
+
if (pItem.CFI && tmpMediaViewer._activeRendition)
|
|
649
|
+
{
|
|
650
|
+
tmpMediaViewer._activeRendition.display(pItem.CFI);
|
|
651
|
+
}
|
|
652
|
+
else if (pItem.PageNumber && typeof tmpMediaViewer._renderPdfPage === 'function')
|
|
653
|
+
{
|
|
654
|
+
tmpMediaViewer._renderPdfPage(pItem.PageNumber);
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}, 1000);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
else if (pItem.Type === 'image-crop' && pItem.CropRegion)
|
|
661
|
+
{
|
|
662
|
+
let tmpIEX = this.pict.views['RetoldRemote-ImageExplorer'];
|
|
663
|
+
if (tmpIEX)
|
|
664
|
+
{
|
|
665
|
+
tmpIEX.showExplorer(pItem.Path);
|
|
666
|
+
// Zoom to the crop region after the viewer loads
|
|
667
|
+
let tmpCrop = pItem.CropRegion;
|
|
668
|
+
setTimeout(() =>
|
|
669
|
+
{
|
|
670
|
+
if (tmpIEX._osdViewer && tmpIEX._dziData)
|
|
671
|
+
{
|
|
672
|
+
let tmpImageRect = new OpenSeadragon.Rect(tmpCrop.X, tmpCrop.Y, tmpCrop.Width, tmpCrop.Height);
|
|
673
|
+
let tmpViewportRect = tmpIEX._osdViewer.viewport.imageToViewportRectangle(tmpImageRect);
|
|
674
|
+
tmpIEX._osdViewer.viewport.fitBounds(tmpViewportRect);
|
|
675
|
+
}
|
|
676
|
+
}, 800);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
633
679
|
else if (pItem.Type === 'video-frame' && pItem.FrameCacheKey && pItem.FrameFilename)
|
|
634
680
|
{
|
|
635
681
|
// Show the cached frame image directly in the viewer
|
|
@@ -166,6 +166,29 @@ function handleViewerKey(pGalleryNav, pEvent)
|
|
|
166
166
|
pGalleryNav._cycleFitMode();
|
|
167
167
|
break;
|
|
168
168
|
|
|
169
|
+
case 's':
|
|
170
|
+
pEvent.preventDefault();
|
|
171
|
+
{
|
|
172
|
+
let tmpMediaViewer = pGalleryNav.pict.views['RetoldRemote-MediaViewer'];
|
|
173
|
+
if (tmpMediaViewer)
|
|
174
|
+
{
|
|
175
|
+
let tmpViewerMediaType = tmpRemote.CurrentViewerMediaType;
|
|
176
|
+
if (tmpViewerMediaType === 'document')
|
|
177
|
+
{
|
|
178
|
+
// Toggle region selection for EPUB or PDF
|
|
179
|
+
if (typeof tmpMediaViewer.ebookToggleRegionSelect === 'function' && tmpMediaViewer._activeRendition)
|
|
180
|
+
{
|
|
181
|
+
tmpMediaViewer.ebookToggleRegionSelect();
|
|
182
|
+
}
|
|
183
|
+
else if (typeof tmpMediaViewer.pdfToggleRegionSelect === 'function' && tmpMediaViewer._pdfDocument)
|
|
184
|
+
{
|
|
185
|
+
tmpMediaViewer.pdfToggleRegionSelect();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
|
|
169
192
|
case 'Enter':
|
|
170
193
|
pEvent.preventDefault();
|
|
171
194
|
pGalleryNav._streamWithVLC();
|