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
|
@@ -387,8 +387,18 @@ class RetoldRemoteCollectionsPanelView extends libPictView
|
|
|
387
387
|
tmpManager.sortActiveCollection(null, tmpNewDir);
|
|
388
388
|
};
|
|
389
389
|
|
|
390
|
+
let tmpExportBtn = document.createElement('button');
|
|
391
|
+
tmpExportBtn.className = 'retold-remote-collections-export-btn';
|
|
392
|
+
tmpExportBtn.textContent = '\u21e9 Export';
|
|
393
|
+
tmpExportBtn.title = 'Export collection to a folder';
|
|
394
|
+
tmpExportBtn.onclick = () =>
|
|
395
|
+
{
|
|
396
|
+
tmpSelf._showExportDialog(tmpCollection.GUID, tmpCollection.Name);
|
|
397
|
+
};
|
|
398
|
+
|
|
390
399
|
tmpControls.appendChild(tmpSortSelect);
|
|
391
400
|
tmpControls.appendChild(tmpDirBtn);
|
|
401
|
+
tmpControls.appendChild(tmpExportBtn);
|
|
392
402
|
pRoot.appendChild(tmpControls);
|
|
393
403
|
}
|
|
394
404
|
|
|
@@ -986,6 +996,162 @@ class RetoldRemoteCollectionsPanelView extends libPictView
|
|
|
986
996
|
return '\uD83D\uDCC4';
|
|
987
997
|
}
|
|
988
998
|
}
|
|
999
|
+
|
|
1000
|
+
// -- Export Dialog -------------------------------------------------------
|
|
1001
|
+
|
|
1002
|
+
/**
|
|
1003
|
+
* Show the export dialog for a collection.
|
|
1004
|
+
*
|
|
1005
|
+
* @param {string} pGUID - Collection GUID
|
|
1006
|
+
* @param {string} pCollectionName - Collection name (for default folder)
|
|
1007
|
+
*/
|
|
1008
|
+
_showExportDialog(pGUID, pCollectionName)
|
|
1009
|
+
{
|
|
1010
|
+
let tmpSelf = this;
|
|
1011
|
+
|
|
1012
|
+
// Build a default folder name from the collection name
|
|
1013
|
+
let tmpDefaultFolder = (pCollectionName || 'collection-export')
|
|
1014
|
+
.replace(/[<>:"/\\|?*\x00-\x1F]/g, '_')
|
|
1015
|
+
.replace(/\s+/g, '_')
|
|
1016
|
+
.substring(0, 80);
|
|
1017
|
+
|
|
1018
|
+
// Use the current browsed folder as a prefix if available
|
|
1019
|
+
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
1020
|
+
let tmpCurrentFolder = tmpRemote.CurrentBrowsedFolder || '';
|
|
1021
|
+
let tmpDefaultPath = tmpCurrentFolder
|
|
1022
|
+
? tmpCurrentFolder.replace(/\/+$/, '') + '/' + tmpDefaultFolder
|
|
1023
|
+
: tmpDefaultFolder;
|
|
1024
|
+
|
|
1025
|
+
// Create a simple inline prompt in the collections detail area
|
|
1026
|
+
let tmpRoot = document.getElementById('RetoldRemote-Collections-Detail');
|
|
1027
|
+
if (!tmpRoot)
|
|
1028
|
+
{
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// Find or create the export dialog container
|
|
1033
|
+
let tmpExistingDialog = document.getElementById('RetoldRemote-ExportDialog');
|
|
1034
|
+
if (tmpExistingDialog)
|
|
1035
|
+
{
|
|
1036
|
+
tmpExistingDialog.parentElement.removeChild(tmpExistingDialog);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
let tmpDialog = document.createElement('div');
|
|
1040
|
+
tmpDialog.id = 'RetoldRemote-ExportDialog';
|
|
1041
|
+
tmpDialog.style.cssText = 'padding:10px;border-top:1px solid var(--retold-border);background:var(--retold-bg-secondary,#21252b);';
|
|
1042
|
+
|
|
1043
|
+
tmpDialog.innerHTML =
|
|
1044
|
+
'<div style="font-size:0.78rem;color:var(--retold-text);margin-bottom:6px;">Export to folder (within content root):</div>'
|
|
1045
|
+
+ '<input type="text" id="RetoldRemote-ExportPath" value="' + tmpDefaultPath.replace(/"/g, '"') + '" '
|
|
1046
|
+
+ 'style="width:100%;box-sizing:border-box;background:var(--retold-bg-input,#1e1e1e);color:var(--retold-text);border:1px solid var(--retold-border);border-radius:4px;padding:4px 8px;font-size:0.78rem;font-family:inherit;margin-bottom:6px;">'
|
|
1047
|
+
+ '<div style="display:flex;gap:6px;">'
|
|
1048
|
+
+ '<button id="RetoldRemote-ExportConfirmBtn" style="flex:1;padding:4px 8px;font-size:0.75rem;">Export</button>'
|
|
1049
|
+
+ '<button id="RetoldRemote-ExportCancelBtn" style="padding:4px 8px;font-size:0.75rem;">Cancel</button>'
|
|
1050
|
+
+ '</div>'
|
|
1051
|
+
+ '<div id="RetoldRemote-ExportStatus" style="font-size:0.72rem;color:var(--retold-text-dim);margin-top:6px;display:none;"></div>';
|
|
1052
|
+
|
|
1053
|
+
tmpRoot.appendChild(tmpDialog);
|
|
1054
|
+
|
|
1055
|
+
// Focus the path input
|
|
1056
|
+
let tmpPathInput = document.getElementById('RetoldRemote-ExportPath');
|
|
1057
|
+
if (tmpPathInput)
|
|
1058
|
+
{
|
|
1059
|
+
tmpPathInput.focus();
|
|
1060
|
+
tmpPathInput.select();
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
// Cancel handler
|
|
1064
|
+
document.getElementById('RetoldRemote-ExportCancelBtn').onclick = () =>
|
|
1065
|
+
{
|
|
1066
|
+
tmpDialog.parentElement.removeChild(tmpDialog);
|
|
1067
|
+
};
|
|
1068
|
+
|
|
1069
|
+
// Export handler
|
|
1070
|
+
document.getElementById('RetoldRemote-ExportConfirmBtn').onclick = () =>
|
|
1071
|
+
{
|
|
1072
|
+
let tmpDestPath = tmpPathInput ? tmpPathInput.value.trim() : '';
|
|
1073
|
+
if (!tmpDestPath)
|
|
1074
|
+
{
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
let tmpBtn = document.getElementById('RetoldRemote-ExportConfirmBtn');
|
|
1079
|
+
let tmpStatus = document.getElementById('RetoldRemote-ExportStatus');
|
|
1080
|
+
if (tmpBtn) tmpBtn.disabled = true;
|
|
1081
|
+
if (tmpBtn) tmpBtn.textContent = 'Exporting\u2026';
|
|
1082
|
+
if (tmpStatus) tmpStatus.style.display = '';
|
|
1083
|
+
if (tmpStatus) tmpStatus.textContent = 'Exporting\u2026';
|
|
1084
|
+
|
|
1085
|
+
fetch('/api/collections/' + encodeURIComponent(pGUID) + '/export',
|
|
1086
|
+
{
|
|
1087
|
+
method: 'POST',
|
|
1088
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1089
|
+
body: JSON.stringify({ DestinationPath: tmpDestPath })
|
|
1090
|
+
})
|
|
1091
|
+
.then((pResponse) => pResponse.json())
|
|
1092
|
+
.then((pResult) =>
|
|
1093
|
+
{
|
|
1094
|
+
if (pResult && pResult.Success)
|
|
1095
|
+
{
|
|
1096
|
+
let tmpMsg = 'Exported ' + pResult.ExportedCount + ' of ' + pResult.TotalItems + ' items';
|
|
1097
|
+
if (pResult.ErrorCount > 0)
|
|
1098
|
+
{
|
|
1099
|
+
tmpMsg += ' (' + pResult.ErrorCount + ' errors)';
|
|
1100
|
+
}
|
|
1101
|
+
tmpMsg += ' to ' + pResult.DestinationPath;
|
|
1102
|
+
|
|
1103
|
+
if (tmpStatus) tmpStatus.textContent = tmpMsg;
|
|
1104
|
+
if (tmpBtn) tmpBtn.textContent = 'Done';
|
|
1105
|
+
|
|
1106
|
+
let tmpToast = tmpSelf.pict.providers['RetoldRemote-ToastNotification'];
|
|
1107
|
+
if (tmpToast)
|
|
1108
|
+
{
|
|
1109
|
+
tmpToast.showToast(tmpMsg);
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
// Auto-dismiss after a moment
|
|
1113
|
+
setTimeout(() =>
|
|
1114
|
+
{
|
|
1115
|
+
if (tmpDialog.parentElement)
|
|
1116
|
+
{
|
|
1117
|
+
tmpDialog.parentElement.removeChild(tmpDialog);
|
|
1118
|
+
}
|
|
1119
|
+
}, 3000);
|
|
1120
|
+
}
|
|
1121
|
+
else
|
|
1122
|
+
{
|
|
1123
|
+
let tmpErrMsg = (pResult && pResult.Error) || 'Export failed';
|
|
1124
|
+
if (tmpStatus) tmpStatus.textContent = tmpErrMsg;
|
|
1125
|
+
if (tmpStatus) tmpStatus.style.color = '#e06c75';
|
|
1126
|
+
if (tmpBtn) tmpBtn.textContent = 'Retry';
|
|
1127
|
+
if (tmpBtn) tmpBtn.disabled = false;
|
|
1128
|
+
}
|
|
1129
|
+
})
|
|
1130
|
+
.catch((pError) =>
|
|
1131
|
+
{
|
|
1132
|
+
if (tmpStatus) tmpStatus.textContent = 'Request failed: ' + pError.message;
|
|
1133
|
+
if (tmpStatus) tmpStatus.style.color = '#e06c75';
|
|
1134
|
+
if (tmpBtn) tmpBtn.textContent = 'Retry';
|
|
1135
|
+
if (tmpBtn) tmpBtn.disabled = false;
|
|
1136
|
+
});
|
|
1137
|
+
};
|
|
1138
|
+
|
|
1139
|
+
// Enter key in input triggers export
|
|
1140
|
+
if (tmpPathInput)
|
|
1141
|
+
{
|
|
1142
|
+
tmpPathInput.onkeydown = (pEvent) =>
|
|
1143
|
+
{
|
|
1144
|
+
if (pEvent.key === 'Enter')
|
|
1145
|
+
{
|
|
1146
|
+
document.getElementById('RetoldRemote-ExportConfirmBtn').click();
|
|
1147
|
+
}
|
|
1148
|
+
if (pEvent.key === 'Escape')
|
|
1149
|
+
{
|
|
1150
|
+
tmpDialog.parentElement.removeChild(tmpDialog);
|
|
1151
|
+
}
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
989
1155
|
}
|
|
990
1156
|
|
|
991
1157
|
RetoldRemoteCollectionsPanelView.default_configuration = _ViewConfiguration;
|