retold-remote 0.0.7 → 0.0.10
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 +0 -2
- package/package.json +1 -1
- package/source/Pict-Application-RetoldRemote.js +30 -11
- package/source/providers/Pict-Provider-CollectionManager.js +302 -11
- package/source/providers/Pict-Provider-GalleryNavigation.js +3 -2
- package/source/providers/Pict-Provider-RetoldRemoteIcons.js +16 -0
- package/source/providers/Pict-Provider-RetoldRemoteTheme.js +52 -0
- package/source/providers/keyboard-handlers/KeyHandler-AudioExplorer.js +11 -0
- package/source/providers/keyboard-handlers/KeyHandler-Gallery.js +11 -0
- package/source/providers/keyboard-handlers/KeyHandler-VideoExplorer.js +11 -0
- package/source/providers/keyboard-handlers/KeyHandler-Viewer.js +11 -0
- package/source/views/PictView-Remote-Layout.js +248 -54
- package/source/views/PictView-Remote-TopBar.js +176 -16
- package/source/views/PictView-Remote-VLCSetup.js +80 -1
- package/web-application/css/retold-remote.css +0 -2
- package/web-application/retold-remote.js +350 -96
- package/web-application/retold-remote.js.map +1 -1
- package/web-application/retold-remote.min.js +14 -14
- package/web-application/retold-remote.min.js.map +1 -1
package/css/retold-remote.css
CHANGED
package/package.json
CHANGED
|
@@ -13,6 +13,8 @@ const libProviderToastNotification = require('./providers/Pict-Provider-ToastNot
|
|
|
13
13
|
const libProviderCollectionManager = require('./providers/Pict-Provider-CollectionManager.js');
|
|
14
14
|
const libProviderAISortManager = require('./providers/Pict-Provider-AISortManager.js');
|
|
15
15
|
|
|
16
|
+
const libExtensionMaps = require('./RetoldRemote-ExtensionMaps.js');
|
|
17
|
+
|
|
16
18
|
// Views (replace parent views)
|
|
17
19
|
const libViewLayout = require('./views/PictView-Remote-Layout.js');
|
|
18
20
|
const libViewTopBar = require('./views/PictView-Remote-TopBar.js');
|
|
@@ -149,6 +151,11 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
149
151
|
BrowsingCollection: false, // true when viewer is navigating collection items
|
|
150
152
|
BrowsingCollectionIndex: -1, // index into ActiveCollection.Items
|
|
151
153
|
|
|
154
|
+
// Favorites
|
|
155
|
+
FavoritesGUID: null,
|
|
156
|
+
FavoritesCollection: null,
|
|
157
|
+
FavoritesPathSet: {}, // path → itemID for O(1) favorited-state checks
|
|
158
|
+
|
|
152
159
|
// AI Sort settings
|
|
153
160
|
AISortSettings:
|
|
154
161
|
{
|
|
@@ -206,11 +213,14 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
206
213
|
// Render the collections panel (starts collapsed)
|
|
207
214
|
this.pict.views['RetoldRemote-CollectionsPanel'].render();
|
|
208
215
|
|
|
209
|
-
// Fetch collections list
|
|
216
|
+
// Fetch collections list and ensure favorites collection exists
|
|
210
217
|
let tmpCollManager = this.pict.providers['RetoldRemote-CollectionManager'];
|
|
211
218
|
if (tmpCollManager)
|
|
212
219
|
{
|
|
213
|
-
tmpCollManager.fetchCollections()
|
|
220
|
+
tmpCollManager.fetchCollections(() =>
|
|
221
|
+
{
|
|
222
|
+
tmpCollManager.ensureFavoritesCollection();
|
|
223
|
+
});
|
|
214
224
|
}
|
|
215
225
|
|
|
216
226
|
// Update the collections button icon
|
|
@@ -306,19 +316,17 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
306
316
|
}
|
|
307
317
|
|
|
308
318
|
/**
|
|
309
|
-
* Override _getMediaType to
|
|
319
|
+
* Override _getMediaType to use comprehensive extension maps.
|
|
320
|
+
*
|
|
321
|
+
* The parent class has a hardcoded subset of video/audio/image extensions.
|
|
322
|
+
* We use RetoldRemote-ExtensionMaps which covers many more formats
|
|
323
|
+
* (mpg, mpeg, ts, mts, 3gp, heic, etc.).
|
|
310
324
|
*
|
|
311
325
|
* @param {string} pExtension - Lowercase extension without dot
|
|
312
|
-
* @returns {string} 'image', 'video', 'audio', 'document', or 'other'
|
|
326
|
+
* @returns {string} 'image', 'video', 'audio', 'document', 'text', or 'other'
|
|
313
327
|
*/
|
|
314
328
|
_getMediaType(pExtension)
|
|
315
329
|
{
|
|
316
|
-
let tmpDocumentExtensions = { 'pdf': true, 'epub': true, 'mobi': true };
|
|
317
|
-
if (tmpDocumentExtensions[pExtension])
|
|
318
|
-
{
|
|
319
|
-
return 'document';
|
|
320
|
-
}
|
|
321
|
-
|
|
322
330
|
let tmpTextExtensions =
|
|
323
331
|
{
|
|
324
332
|
'js': true, 'mjs': true, 'cjs': true, 'ts': true, 'tsx': true, 'jsx': true,
|
|
@@ -338,7 +346,8 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
338
346
|
return 'text';
|
|
339
347
|
}
|
|
340
348
|
|
|
341
|
-
|
|
349
|
+
// Use the comprehensive extension maps (covers mpg, mpeg, ts, heic, etc.)
|
|
350
|
+
return libExtensionMaps.getCategory(pExtension);
|
|
342
351
|
}
|
|
343
352
|
|
|
344
353
|
/**
|
|
@@ -409,6 +418,10 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
409
418
|
if (tmpTopBar)
|
|
410
419
|
{
|
|
411
420
|
tmpTopBar.updateInfo();
|
|
421
|
+
if (typeof tmpTopBar.updateFavoritesIcon === 'function')
|
|
422
|
+
{
|
|
423
|
+
tmpTopBar.updateFavoritesIcon();
|
|
424
|
+
}
|
|
412
425
|
}
|
|
413
426
|
}
|
|
414
427
|
|
|
@@ -447,6 +460,10 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
447
460
|
if (tmpTopBar)
|
|
448
461
|
{
|
|
449
462
|
tmpTopBar.updateInfo();
|
|
463
|
+
if (typeof tmpTopBar.updateFavoritesIcon === 'function')
|
|
464
|
+
{
|
|
465
|
+
tmpTopBar.updateFavoritesIcon();
|
|
466
|
+
}
|
|
450
467
|
}
|
|
451
468
|
}
|
|
452
469
|
|
|
@@ -758,6 +775,7 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
758
775
|
CollectionsPanelOpen: tmpRemote.CollectionsPanelOpen,
|
|
759
776
|
CollectionsPanelWidth: tmpRemote.CollectionsPanelWidth,
|
|
760
777
|
LastUsedCollectionGUID: tmpRemote.LastUsedCollectionGUID,
|
|
778
|
+
FavoritesGUID: tmpRemote.FavoritesGUID,
|
|
761
779
|
AISortSettings: tmpRemote.AISortSettings
|
|
762
780
|
};
|
|
763
781
|
localStorage.setItem('retold-remote-settings', JSON.stringify(tmpSettings));
|
|
@@ -806,6 +824,7 @@ class RetoldRemoteApplication extends libContentEditorApplication
|
|
|
806
824
|
if (typeof (tmpSettings.CollectionsPanelOpen) === 'boolean') tmpRemote.CollectionsPanelOpen = tmpSettings.CollectionsPanelOpen;
|
|
807
825
|
if (tmpSettings.CollectionsPanelWidth) tmpRemote.CollectionsPanelWidth = tmpSettings.CollectionsPanelWidth;
|
|
808
826
|
if (tmpSettings.LastUsedCollectionGUID) tmpRemote.LastUsedCollectionGUID = tmpSettings.LastUsedCollectionGUID;
|
|
827
|
+
if (tmpSettings.FavoritesGUID) tmpRemote.FavoritesGUID = tmpSettings.FavoritesGUID;
|
|
809
828
|
if (tmpSettings.AISortSettings && typeof tmpSettings.AISortSettings === 'object')
|
|
810
829
|
{
|
|
811
830
|
if (tmpSettings.AISortSettings.AIEndpoint) tmpRemote.AISortSettings.AIEndpoint = tmpSettings.AISortSettings.AIEndpoint;
|
|
@@ -158,7 +158,7 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
158
158
|
let tmpToast = tmpSelf._getToast();
|
|
159
159
|
if (tmpToast)
|
|
160
160
|
{
|
|
161
|
-
tmpToast.
|
|
161
|
+
tmpToast.showToast('Collection created: ' + (pData.Name || pName));
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
return tmpCallback(null, pData);
|
|
@@ -255,7 +255,7 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
255
255
|
let tmpToast = tmpSelf._getToast();
|
|
256
256
|
if (tmpToast)
|
|
257
257
|
{
|
|
258
|
-
tmpToast.
|
|
258
|
+
tmpToast.showToast('Collection deleted');
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
return tmpCallback(null);
|
|
@@ -310,7 +310,7 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
310
310
|
if (tmpToast)
|
|
311
311
|
{
|
|
312
312
|
let tmpCollectionName = pData.Name || 'collection';
|
|
313
|
-
tmpToast.
|
|
313
|
+
tmpToast.showToast('Added ' + pItems.length + ' item' + (pItems.length > 1 ? 's' : '') + ' to ' + tmpCollectionName);
|
|
314
314
|
}
|
|
315
315
|
|
|
316
316
|
return tmpCallback(null, pData);
|
|
@@ -443,7 +443,7 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
443
443
|
let tmpToast = tmpSelf._getToast();
|
|
444
444
|
if (tmpToast)
|
|
445
445
|
{
|
|
446
|
-
tmpToast.
|
|
446
|
+
tmpToast.showToast('Copied ' + pItemIDs.length + ' item' + (pItemIDs.length > 1 ? 's' : ''));
|
|
447
447
|
}
|
|
448
448
|
|
|
449
449
|
return tmpCallback(null, pData);
|
|
@@ -463,6 +463,39 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
463
463
|
togglePanel()
|
|
464
464
|
{
|
|
465
465
|
let tmpRemote = this._getRemote();
|
|
466
|
+
|
|
467
|
+
// On mobile, delegate to the sidebar collections tab instead of the right-side panel
|
|
468
|
+
let tmpLayoutView = this.pict.views['ContentEditor-Layout'];
|
|
469
|
+
if (tmpLayoutView && tmpLayoutView.isMobileDrawer())
|
|
470
|
+
{
|
|
471
|
+
// Check if collections tab is already active
|
|
472
|
+
let tmpActiveTab = document.querySelector('.content-editor-sidebar-tab.active');
|
|
473
|
+
let tmpIsCollectionsActive = tmpActiveTab && tmpActiveTab.getAttribute('data-tab') === 'collections';
|
|
474
|
+
|
|
475
|
+
if (tmpIsCollectionsActive)
|
|
476
|
+
{
|
|
477
|
+
// Switch back to files tab
|
|
478
|
+
tmpLayoutView.switchSidebarTab('files');
|
|
479
|
+
}
|
|
480
|
+
else
|
|
481
|
+
{
|
|
482
|
+
// Open sidebar if collapsed, then switch to collections tab
|
|
483
|
+
if (tmpRemote.SidebarCollapsed)
|
|
484
|
+
{
|
|
485
|
+
tmpLayoutView.toggleSidebar();
|
|
486
|
+
}
|
|
487
|
+
tmpLayoutView.switchSidebarTab('collections');
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Update topbar button state
|
|
491
|
+
let tmpTopBar = this.pict.views['ContentEditor-TopBar'];
|
|
492
|
+
if (tmpTopBar && typeof tmpTopBar.updateCollectionsIcon === 'function')
|
|
493
|
+
{
|
|
494
|
+
tmpTopBar.updateCollectionsIcon();
|
|
495
|
+
}
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
466
499
|
tmpRemote.CollectionsPanelOpen = !tmpRemote.CollectionsPanelOpen;
|
|
467
500
|
|
|
468
501
|
let tmpWrap = document.getElementById('RetoldRemote-Collections-Wrap');
|
|
@@ -531,6 +564,264 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
531
564
|
}
|
|
532
565
|
}
|
|
533
566
|
|
|
567
|
+
// -- Favorites Methods ------------------------------------------------
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Ensure the favorites collection exists.
|
|
571
|
+
* If FavoritesGUID is set, loads it. Otherwise searches existing
|
|
572
|
+
* collections for CollectionType === 'favorites'. If not found,
|
|
573
|
+
* creates one with a well-known GUID.
|
|
574
|
+
*
|
|
575
|
+
* @param {Function} [fCallback] - Optional callback(pError)
|
|
576
|
+
*/
|
|
577
|
+
ensureFavoritesCollection(fCallback)
|
|
578
|
+
{
|
|
579
|
+
let tmpSelf = this;
|
|
580
|
+
let tmpCallback = (typeof fCallback === 'function') ? fCallback : () => {};
|
|
581
|
+
let tmpRemote = this._getRemote();
|
|
582
|
+
|
|
583
|
+
// If we already have a GUID, just load it
|
|
584
|
+
if (tmpRemote.FavoritesGUID)
|
|
585
|
+
{
|
|
586
|
+
return this._loadFavoritesCollection(tmpRemote.FavoritesGUID, tmpCallback);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// Search existing collections for a favorites-type collection
|
|
590
|
+
let tmpCollections = tmpRemote.Collections || [];
|
|
591
|
+
for (let i = 0; i < tmpCollections.length; i++)
|
|
592
|
+
{
|
|
593
|
+
if (tmpCollections[i].CollectionType === 'favorites')
|
|
594
|
+
{
|
|
595
|
+
tmpRemote.FavoritesGUID = tmpCollections[i].GUID;
|
|
596
|
+
this.pict.PictApplication.saveSettings();
|
|
597
|
+
return this._loadFavoritesCollection(tmpCollections[i].GUID, tmpCallback);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Not found — create one
|
|
602
|
+
let tmpGUID = 'favorites-default';
|
|
603
|
+
|
|
604
|
+
fetch('/api/collections/' + encodeURIComponent(tmpGUID),
|
|
605
|
+
{
|
|
606
|
+
method: 'PUT',
|
|
607
|
+
headers: { 'Content-Type': 'application/json' },
|
|
608
|
+
body: JSON.stringify({ Name: 'Favorites', CollectionType: 'favorites', Icon: 'heart' })
|
|
609
|
+
})
|
|
610
|
+
.then((pResponse) => pResponse.json())
|
|
611
|
+
.then((pData) =>
|
|
612
|
+
{
|
|
613
|
+
tmpRemote.FavoritesGUID = tmpGUID;
|
|
614
|
+
tmpSelf.pict.PictApplication.saveSettings();
|
|
615
|
+
tmpSelf._loadFavoritesCollection(tmpGUID, tmpCallback);
|
|
616
|
+
})
|
|
617
|
+
.catch((pError) =>
|
|
618
|
+
{
|
|
619
|
+
tmpSelf.log.error('Failed to create favorites collection: ' + pError.message);
|
|
620
|
+
return tmpCallback(pError);
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Load the favorites collection and rebuild the path set.
|
|
626
|
+
*
|
|
627
|
+
* @param {string} pGUID - Collection GUID
|
|
628
|
+
* @param {Function} [fCallback] - Optional callback(pError)
|
|
629
|
+
*/
|
|
630
|
+
_loadFavoritesCollection(pGUID, fCallback)
|
|
631
|
+
{
|
|
632
|
+
let tmpSelf = this;
|
|
633
|
+
let tmpCallback = (typeof fCallback === 'function') ? fCallback : () => {};
|
|
634
|
+
|
|
635
|
+
fetch('/api/collections/' + encodeURIComponent(pGUID))
|
|
636
|
+
.then((pResponse) =>
|
|
637
|
+
{
|
|
638
|
+
if (!pResponse.ok)
|
|
639
|
+
{
|
|
640
|
+
throw new Error('Favorites collection not found');
|
|
641
|
+
}
|
|
642
|
+
return pResponse.json();
|
|
643
|
+
})
|
|
644
|
+
.then((pData) =>
|
|
645
|
+
{
|
|
646
|
+
let tmpRemote = tmpSelf._getRemote();
|
|
647
|
+
tmpRemote.FavoritesCollection = pData;
|
|
648
|
+
tmpSelf._rebuildFavoritesPathSet(pData);
|
|
649
|
+
|
|
650
|
+
// Update heart icon in topbar
|
|
651
|
+
let tmpTopBar = tmpSelf.pict.views['ContentEditor-TopBar'];
|
|
652
|
+
if (tmpTopBar && typeof tmpTopBar.updateFavoritesIcon === 'function')
|
|
653
|
+
{
|
|
654
|
+
tmpTopBar.updateFavoritesIcon();
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Update favorites pane if visible
|
|
658
|
+
tmpSelf._renderFavoritesPane();
|
|
659
|
+
|
|
660
|
+
return tmpCallback(null);
|
|
661
|
+
})
|
|
662
|
+
.catch((pError) =>
|
|
663
|
+
{
|
|
664
|
+
tmpSelf.log.error('Failed to load favorites collection: ' + pError.message);
|
|
665
|
+
return tmpCallback(pError);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Rebuild the FavoritesPathSet from a collection's Items array.
|
|
671
|
+
*
|
|
672
|
+
* @param {Object} pCollection - Collection object with Items array
|
|
673
|
+
*/
|
|
674
|
+
_rebuildFavoritesPathSet(pCollection)
|
|
675
|
+
{
|
|
676
|
+
let tmpRemote = this._getRemote();
|
|
677
|
+
tmpRemote.FavoritesPathSet = {};
|
|
678
|
+
|
|
679
|
+
if (!pCollection || !Array.isArray(pCollection.Items))
|
|
680
|
+
{
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
for (let i = 0; i < pCollection.Items.length; i++)
|
|
685
|
+
{
|
|
686
|
+
let tmpItem = pCollection.Items[i];
|
|
687
|
+
if (tmpItem.Path)
|
|
688
|
+
{
|
|
689
|
+
tmpRemote.FavoritesPathSet[tmpItem.Path] = tmpItem.ID;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
/**
|
|
695
|
+
* Check if a file path is in favorites.
|
|
696
|
+
*
|
|
697
|
+
* @param {string} [pPath] - File path (defaults to currently viewed file)
|
|
698
|
+
* @returns {boolean} True if favorited
|
|
699
|
+
*/
|
|
700
|
+
isFavorited(pPath)
|
|
701
|
+
{
|
|
702
|
+
let tmpRemote = this._getRemote();
|
|
703
|
+
let tmpFilePath = pPath || this.pict.AppData.ContentEditor.CurrentFile;
|
|
704
|
+
return !!(tmpRemote.FavoritesPathSet[tmpFilePath]);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Toggle a file in/out of favorites.
|
|
709
|
+
*
|
|
710
|
+
* @param {string} [pPath] - File path (defaults to currently viewed file)
|
|
711
|
+
*/
|
|
712
|
+
toggleFavorite(pPath)
|
|
713
|
+
{
|
|
714
|
+
let tmpSelf = this;
|
|
715
|
+
let tmpRemote = this._getRemote();
|
|
716
|
+
let tmpFilePath = pPath || this.pict.AppData.ContentEditor.CurrentFile;
|
|
717
|
+
|
|
718
|
+
if (!tmpFilePath)
|
|
719
|
+
{
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
if (!tmpRemote.FavoritesGUID)
|
|
724
|
+
{
|
|
725
|
+
// Favorites collection not ready yet
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (this.isFavorited(tmpFilePath))
|
|
730
|
+
{
|
|
731
|
+
// Remove from favorites
|
|
732
|
+
let tmpItemID = tmpRemote.FavoritesPathSet[tmpFilePath];
|
|
733
|
+
this.removeItemFromCollection(tmpRemote.FavoritesGUID, tmpItemID, (pError, pData) =>
|
|
734
|
+
{
|
|
735
|
+
if (!pError && pData)
|
|
736
|
+
{
|
|
737
|
+
tmpRemote.FavoritesCollection = pData;
|
|
738
|
+
tmpSelf._rebuildFavoritesPathSet(pData);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
// Update heart icon
|
|
742
|
+
let tmpTopBar = tmpSelf.pict.views['ContentEditor-TopBar'];
|
|
743
|
+
if (tmpTopBar && typeof tmpTopBar.updateFavoritesIcon === 'function')
|
|
744
|
+
{
|
|
745
|
+
tmpTopBar.updateFavoritesIcon();
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
tmpSelf._renderFavoritesPane();
|
|
749
|
+
|
|
750
|
+
let tmpToast = tmpSelf._getToast();
|
|
751
|
+
if (tmpToast)
|
|
752
|
+
{
|
|
753
|
+
tmpToast.showToast('Removed from favorites');
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
else
|
|
758
|
+
{
|
|
759
|
+
// Add to favorites — build item using same logic as addCurrentFileToCollection
|
|
760
|
+
let tmpItem =
|
|
761
|
+
{
|
|
762
|
+
Type: 'file',
|
|
763
|
+
Path: tmpFilePath,
|
|
764
|
+
Label: '',
|
|
765
|
+
Note: ''
|
|
766
|
+
};
|
|
767
|
+
|
|
768
|
+
// Detect archive subfile
|
|
769
|
+
let tmpArchiveMatch = tmpFilePath.match(/^(.*?\.(zip|7z|rar|tar|tgz|cbz|cbr|tar\.gz|tar\.bz2|tar\.xz))\/(.*)/i);
|
|
770
|
+
if (tmpArchiveMatch)
|
|
771
|
+
{
|
|
772
|
+
tmpItem.Type = 'subfile';
|
|
773
|
+
tmpItem.ArchivePath = tmpArchiveMatch[1];
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Include hash if available
|
|
777
|
+
let tmpProvider = this.pict.providers['RetoldRemote-Provider'];
|
|
778
|
+
if (tmpProvider)
|
|
779
|
+
{
|
|
780
|
+
let tmpHash = tmpProvider.getHashForPath(tmpFilePath);
|
|
781
|
+
if (tmpHash)
|
|
782
|
+
{
|
|
783
|
+
tmpItem.Hash = tmpHash;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
this.addItemsToCollection(tmpRemote.FavoritesGUID, [tmpItem], (pError, pData) =>
|
|
788
|
+
{
|
|
789
|
+
if (!pError && pData)
|
|
790
|
+
{
|
|
791
|
+
tmpRemote.FavoritesCollection = pData;
|
|
792
|
+
tmpSelf._rebuildFavoritesPathSet(pData);
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Update heart icon
|
|
796
|
+
let tmpTopBar = tmpSelf.pict.views['ContentEditor-TopBar'];
|
|
797
|
+
if (tmpTopBar && typeof tmpTopBar.updateFavoritesIcon === 'function')
|
|
798
|
+
{
|
|
799
|
+
tmpTopBar.updateFavoritesIcon();
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
tmpSelf._renderFavoritesPane();
|
|
803
|
+
|
|
804
|
+
let tmpToast = tmpSelf._getToast();
|
|
805
|
+
if (tmpToast)
|
|
806
|
+
{
|
|
807
|
+
tmpToast.showToast('Added to favorites');
|
|
808
|
+
}
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Render the favorites pane if the favorites tab is active.
|
|
815
|
+
*/
|
|
816
|
+
_renderFavoritesPane()
|
|
817
|
+
{
|
|
818
|
+
let tmpLayoutView = this.pict.views['ContentEditor-Layout'];
|
|
819
|
+
if (tmpLayoutView && typeof tmpLayoutView.renderFavoritesList === 'function')
|
|
820
|
+
{
|
|
821
|
+
tmpLayoutView.renderFavoritesList();
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
534
825
|
// -- Convenience Methods ----------------------------------------------
|
|
535
826
|
|
|
536
827
|
/**
|
|
@@ -985,7 +1276,7 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
985
1276
|
let tmpToast = tmpSelf._getToast();
|
|
986
1277
|
if (tmpToast)
|
|
987
1278
|
{
|
|
988
|
-
tmpToast.
|
|
1279
|
+
tmpToast.showToast('Sort plan created: ' + (pData.Name || pName));
|
|
989
1280
|
}
|
|
990
1281
|
|
|
991
1282
|
return tmpCallback(null, pData);
|
|
@@ -1035,11 +1326,11 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
1035
1326
|
{
|
|
1036
1327
|
if (pData.TotalFailed > 0)
|
|
1037
1328
|
{
|
|
1038
|
-
tmpToast.
|
|
1329
|
+
tmpToast.showToast('Moved ' + pData.TotalMoved + ' files (' + pData.TotalFailed + ' failed)');
|
|
1039
1330
|
}
|
|
1040
1331
|
else
|
|
1041
1332
|
{
|
|
1042
|
-
tmpToast.
|
|
1333
|
+
tmpToast.showToast('Successfully moved ' + pData.TotalMoved + ' files');
|
|
1043
1334
|
}
|
|
1044
1335
|
}
|
|
1045
1336
|
|
|
@@ -1051,7 +1342,7 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
1051
1342
|
let tmpToast = tmpSelf._getToast();
|
|
1052
1343
|
if (tmpToast)
|
|
1053
1344
|
{
|
|
1054
|
-
tmpToast.
|
|
1345
|
+
tmpToast.showToast('Failed to execute operations: ' + pError.message);
|
|
1055
1346
|
}
|
|
1056
1347
|
return tmpCallback(pError);
|
|
1057
1348
|
});
|
|
@@ -1075,7 +1366,7 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
1075
1366
|
let tmpToast = tmpSelf._getToast();
|
|
1076
1367
|
if (tmpToast)
|
|
1077
1368
|
{
|
|
1078
|
-
tmpToast.
|
|
1369
|
+
tmpToast.showToast('No batch to undo');
|
|
1079
1370
|
}
|
|
1080
1371
|
return tmpCallback(new Error('No batch to undo'));
|
|
1081
1372
|
}
|
|
@@ -1114,7 +1405,7 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
1114
1405
|
let tmpToast = tmpSelf._getToast();
|
|
1115
1406
|
if (tmpToast)
|
|
1116
1407
|
{
|
|
1117
|
-
tmpToast.
|
|
1408
|
+
tmpToast.showToast('Undo complete: ' + pData.TotalReversed + ' files restored');
|
|
1118
1409
|
}
|
|
1119
1410
|
|
|
1120
1411
|
return tmpCallback(null, pData);
|
|
@@ -1125,7 +1416,7 @@ class CollectionManagerProvider extends libPictProvider
|
|
|
1125
1416
|
let tmpToast = tmpSelf._getToast();
|
|
1126
1417
|
if (tmpToast)
|
|
1127
1418
|
{
|
|
1128
|
-
tmpToast.
|
|
1419
|
+
tmpToast.showToast('Failed to undo: ' + pError.message);
|
|
1129
1420
|
}
|
|
1130
1421
|
return tmpCallback(pError);
|
|
1131
1422
|
});
|
|
@@ -1277,10 +1277,11 @@ class GalleryNavigationProvider extends libPictProvider
|
|
|
1277
1277
|
let tmpProvider = this.pict.providers['RetoldRemote-Provider'];
|
|
1278
1278
|
let tmpContentPath = tmpProvider ? tmpProvider.getContentURL(tmpFilePath) : ('/content/' + encodeURIComponent(tmpFilePath));
|
|
1279
1279
|
let tmpStreamURL = window.location.origin + tmpContentPath;
|
|
1280
|
-
// On Windows, VLC
|
|
1280
|
+
// On Windows and mobile (iOS/Android), VLC handles the raw URL natively.
|
|
1281
1281
|
// On macOS/Linux our custom handlers URL-decode, so we encode.
|
|
1282
1282
|
let tmpIsWindows = /Windows/.test(navigator.userAgent);
|
|
1283
|
-
let
|
|
1283
|
+
let tmpIsMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
|
1284
|
+
let tmpVLCURL = (tmpIsWindows || tmpIsMobile)
|
|
1284
1285
|
? ('vlc://' + tmpStreamURL)
|
|
1285
1286
|
: ('vlc://' + encodeURIComponent(tmpStreamURL));
|
|
1286
1287
|
|
|
@@ -470,6 +470,22 @@ class RetoldRemoteIconProvider extends libPictProvider
|
|
|
470
470
|
+ '</svg>';
|
|
471
471
|
},
|
|
472
472
|
|
|
473
|
+
'heart': (pSize) =>
|
|
474
|
+
{
|
|
475
|
+
let c = tmpSelf._colors;
|
|
476
|
+
return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
|
|
477
|
+
+ '<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" stroke="' + c.Primary + '" stroke-width="1.8" fill="none" />'
|
|
478
|
+
+ '</svg>';
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
'heart-filled': (pSize) =>
|
|
482
|
+
{
|
|
483
|
+
let c = tmpSelf._colors;
|
|
484
|
+
return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
|
|
485
|
+
+ '<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" stroke="#e74c3c" stroke-width="1.8" fill="#e74c3c" />'
|
|
486
|
+
+ '</svg>';
|
|
487
|
+
},
|
|
488
|
+
|
|
473
489
|
'drag-handle': (pSize) =>
|
|
474
490
|
{
|
|
475
491
|
let c = tmpSelf._colors;
|
|
@@ -759,6 +759,58 @@ class RetoldRemoteThemeProvider extends libPictProvider
|
|
|
759
759
|
PdfText: '#CC4422'
|
|
760
760
|
}
|
|
761
761
|
});
|
|
762
|
+
|
|
763
|
+
// ===================================================================
|
|
764
|
+
// DEBUG THEME (unique color per container for layout debugging)
|
|
765
|
+
// ===================================================================
|
|
766
|
+
|
|
767
|
+
tmpSelf._addTheme('mobile-debug',
|
|
768
|
+
{
|
|
769
|
+
Name: 'Mobile Container Debug',
|
|
770
|
+
Category: 'Debug',
|
|
771
|
+
Description: 'Unique color per container for layout debugging',
|
|
772
|
+
Variables:
|
|
773
|
+
{
|
|
774
|
+
'--retold-bg-primary': '#FF0000',
|
|
775
|
+
'--retold-bg-secondary': '#00CCCC',
|
|
776
|
+
'--retold-bg-tertiary': '#00AA00',
|
|
777
|
+
'--retold-bg-panel': '#FFAA00',
|
|
778
|
+
'--retold-bg-viewer': '#333333',
|
|
779
|
+
'--retold-bg-hover': 'rgba(255, 255, 255, 0.2)',
|
|
780
|
+
'--retold-bg-selected': 'rgba(255, 255, 255, 0.3)',
|
|
781
|
+
'--retold-bg-thumb': '#AA00AA',
|
|
782
|
+
'--retold-text-primary': '#FFFFFF',
|
|
783
|
+
'--retold-text-secondary': '#EEEEEE',
|
|
784
|
+
'--retold-text-muted': '#CCCCCC',
|
|
785
|
+
'--retold-text-dim': '#AAAAAA',
|
|
786
|
+
'--retold-text-placeholder': '#888888',
|
|
787
|
+
'--retold-accent': '#FFFF00',
|
|
788
|
+
'--retold-accent-hover': '#FFFF88',
|
|
789
|
+
'--retold-border': '#FFFFFF',
|
|
790
|
+
'--retold-border-light': '#CCCCCC',
|
|
791
|
+
'--retold-danger': '#FF0000',
|
|
792
|
+
'--retold-danger-muted': '#CC4444',
|
|
793
|
+
'--retold-scrollbar': '#888888',
|
|
794
|
+
'--retold-scrollbar-hover': '#AAAAAA',
|
|
795
|
+
'--retold-selection-bg': 'rgba(255, 255, 0, 0.3)',
|
|
796
|
+
'--retold-focus-outline': '#FFFF00',
|
|
797
|
+
'--retold-font-family': "system-ui, -apple-system, sans-serif",
|
|
798
|
+
'--retold-font-mono': "'SF Mono', 'Consolas', monospace"
|
|
799
|
+
},
|
|
800
|
+
IconColors:
|
|
801
|
+
{
|
|
802
|
+
Primary: '#FFFFFF',
|
|
803
|
+
Accent: '#FFFF00',
|
|
804
|
+
Muted: '#CCCCCC',
|
|
805
|
+
Light: '#333333',
|
|
806
|
+
WarmBeige: '#FFAA00',
|
|
807
|
+
TealTint: '#00CCCC',
|
|
808
|
+
Lavender: '#AA00AA',
|
|
809
|
+
AmberTint: '#FFAA00',
|
|
810
|
+
PdfFill: '#FF4444',
|
|
811
|
+
PdfText: '#FFFFFF'
|
|
812
|
+
}
|
|
813
|
+
});
|
|
762
814
|
}
|
|
763
815
|
|
|
764
816
|
/**
|
|
@@ -82,6 +82,17 @@ function handleAudioExplorerKey(pGalleryNav, pEvent)
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
break;
|
|
85
|
+
|
|
86
|
+
case 'h':
|
|
87
|
+
pEvent.preventDefault();
|
|
88
|
+
{
|
|
89
|
+
let tmpFavCollManager = pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];
|
|
90
|
+
if (tmpFavCollManager)
|
|
91
|
+
{
|
|
92
|
+
tmpFavCollManager.toggleFavorite();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
break;
|
|
85
96
|
}
|
|
86
97
|
}
|
|
87
98
|
|
|
@@ -184,6 +184,17 @@ function handleGalleryKey(pGalleryNav, pEvent)
|
|
|
184
184
|
}
|
|
185
185
|
break;
|
|
186
186
|
|
|
187
|
+
case 'h':
|
|
188
|
+
pEvent.preventDefault();
|
|
189
|
+
{
|
|
190
|
+
let tmpFavCollManager = pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];
|
|
191
|
+
if (tmpFavCollManager)
|
|
192
|
+
{
|
|
193
|
+
tmpFavCollManager.toggleFavorite();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
break;
|
|
197
|
+
|
|
187
198
|
}
|
|
188
199
|
}
|
|
189
200
|
|
|
@@ -51,6 +51,17 @@ function handleVideoExplorerKey(pGalleryNav, pEvent)
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
break;
|
|
54
|
+
|
|
55
|
+
case 'h':
|
|
56
|
+
pEvent.preventDefault();
|
|
57
|
+
{
|
|
58
|
+
let tmpFavCollManager = pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];
|
|
59
|
+
if (tmpFavCollManager)
|
|
60
|
+
{
|
|
61
|
+
tmpFavCollManager.toggleFavorite();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
54
65
|
}
|
|
55
66
|
}
|
|
56
67
|
|
|
@@ -191,6 +191,17 @@ function handleViewerKey(pGalleryNav, pEvent)
|
|
|
191
191
|
pEvent.preventDefault();
|
|
192
192
|
pGalleryNav.switchViewerType('text');
|
|
193
193
|
break;
|
|
194
|
+
|
|
195
|
+
case 'h':
|
|
196
|
+
pEvent.preventDefault();
|
|
197
|
+
{
|
|
198
|
+
let tmpFavCollManager = pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];
|
|
199
|
+
if (tmpFavCollManager)
|
|
200
|
+
{
|
|
201
|
+
tmpFavCollManager.toggleFavorite();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
break;
|
|
194
205
|
}
|
|
195
206
|
}
|
|
196
207
|
|