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.
Files changed (47) hide show
  1. package/css/retold-remote.css +87 -20
  2. package/docs/README.md +59 -11
  3. package/docs/_sidebar.md +1 -0
  4. package/docs/collections.md +30 -0
  5. package/docs/ebook-reader.md +75 -1
  6. package/docs/image-explorer.md +27 -1
  7. package/docs/server-setup.md +28 -18
  8. package/docs/stack-launcher.md +218 -0
  9. package/docs/ultravisor-integration.md +2 -0
  10. package/package.json +10 -7
  11. package/source/Pict-Application-RetoldRemote.js +2 -0
  12. package/source/RetoldRemote-ExtensionMaps.js +1 -1
  13. package/source/cli/RetoldRemote-Server-Setup.js +240 -2
  14. package/source/cli/RetoldRemote-Stack-Launcher.js +387 -0
  15. package/source/cli/RetoldRemote-Stack-Run.js +41 -0
  16. package/source/cli/commands/RetoldRemote-Command-Serve.js +129 -54
  17. package/source/providers/CollectionManager-AddItems.js +166 -0
  18. package/source/providers/Pict-Provider-GalleryNavigation.js +46 -0
  19. package/source/providers/keyboard-handlers/KeyHandler-ImageExplorer.js +5 -0
  20. package/source/providers/keyboard-handlers/KeyHandler-Viewer.js +23 -0
  21. package/source/server/RetoldRemote-CollectionExportService.js +696 -0
  22. package/source/server/RetoldRemote-CollectionService.js +5 -0
  23. package/source/server/RetoldRemote-EbookService.js +194 -3
  24. package/source/server/RetoldRemote-SubimageService.js +530 -0
  25. package/source/server/RetoldRemote-ToolDetector.js +50 -0
  26. package/source/server/RetoldRemote-UltravisorOperations.js +6 -6
  27. package/source/views/MediaViewer-EbookViewer.js +419 -1
  28. package/source/views/MediaViewer-PdfViewer.js +963 -0
  29. package/source/views/PictView-Remote-CollectionsPanel.js +166 -0
  30. package/source/views/PictView-Remote-ImageExplorer.js +606 -1
  31. package/source/views/PictView-Remote-ImageViewer.js +2 -2
  32. package/source/views/PictView-Remote-Layout.js +12 -0
  33. package/source/views/PictView-Remote-MediaViewer.js +83 -25
  34. package/source/views/PictView-Remote-SubimagesPanel.js +353 -0
  35. package/web-application/css/retold-remote.css +87 -20
  36. package/web-application/docs/README.md +59 -11
  37. package/web-application/docs/_sidebar.md +1 -0
  38. package/web-application/docs/collections.md +30 -0
  39. package/web-application/docs/ebook-reader.md +75 -1
  40. package/web-application/docs/image-explorer.md +27 -1
  41. package/web-application/docs/server-setup.md +28 -18
  42. package/web-application/docs/stack-launcher.md +218 -0
  43. package/web-application/docs/ultravisor-integration.md +2 -0
  44. package/web-application/retold-remote.js +399 -45
  45. package/web-application/retold-remote.js.map +1 -1
  46. package/web-application/retold-remote.min.js +13 -12
  47. package/web-application/retold-remote.min.js.map +1 -1
@@ -1,4 +1,4 @@
1
- "use strict";(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f();}else if(typeof define==="function"&&define.amd){define([],f);}else{var g;if(typeof window!=="undefined"){g=window;}else if(typeof global!=="undefined"){g=global;}else if(typeof self!=="undefined"){g=self;}else{g=this;}g.retoldRemote=f();}})(function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a;}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r);},p,p.exports,r,e,n,t);}return n[i].exports;}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o;}return r;}()({1:[function(require,module,exports){},{}],2:[function(require,module,exports){arguments[4][1][0].apply(exports,arguments);},{"dup":1}],3:[function(require,module,exports){'use strict';var bind=require('function-bind');var $apply=require('./functionApply');var $call=require('./functionCall');var $reflectApply=require('./reflectApply');/** @type {import('./actualApply')} */module.exports=$reflectApply||bind.call($call,$apply);},{"./functionApply":4,"./functionCall":5,"./reflectApply":7,"function-bind":22}],4:[function(require,module,exports){'use strict';/** @type {import('./functionApply')} */module.exports=Function.prototype.apply;},{}],5:[function(require,module,exports){'use strict';/** @type {import('./functionCall')} */module.exports=Function.prototype.call;},{}],6:[function(require,module,exports){'use strict';var bind=require('function-bind');var $TypeError=require('es-errors/type');var $call=require('./functionCall');var $actualApply=require('./actualApply');/** @type {(args: [Function, thisArg?: unknown, ...args: unknown[]]) => Function} TODO FIXME, find a way to use import('.') */module.exports=function callBindBasic(args){if(args.length<1||typeof args[0]!=='function'){throw new $TypeError('a function is required');}return $actualApply(bind,$call,args);};},{"./actualApply":3,"./functionCall":5,"es-errors/type":16,"function-bind":22}],7:[function(require,module,exports){'use strict';/** @type {import('./reflectApply')} */module.exports=typeof Reflect!=='undefined'&&Reflect&&Reflect.apply;},{}],8:[function(require,module,exports){'use strict';var GetIntrinsic=require('get-intrinsic');var callBindBasic=require('call-bind-apply-helpers');/** @type {(thisArg: string, searchString: string, position?: number) => number} */var $indexOf=callBindBasic([GetIntrinsic('%String.prototype.indexOf%')]);/** @type {import('.')} */module.exports=function callBoundIntrinsic(name,allowMissing){/* eslint no-extra-parens: 0 */var intrinsic=/** @type {(this: unknown, ...args: unknown[]) => unknown} */GetIntrinsic(name,!!allowMissing);if(typeof intrinsic==='function'&&$indexOf(name,'.prototype.')>-1){return callBindBasic(/** @type {const} */[intrinsic]);}return intrinsic;};},{"call-bind-apply-helpers":6,"get-intrinsic":23}],9:[function(require,module,exports){'use strict';var callBind=require('call-bind-apply-helpers');var gOPD=require('gopd');var hasProtoAccessor;try{// eslint-disable-next-line no-extra-parens, no-proto
1
+ "use strict";function _interopRequireWildcard(e,t){if("function"==typeof WeakMap)var r=new WeakMap(),n=new WeakMap();return(_interopRequireWildcard=function(e,t){if(!t&&e&&e.__esModule)return e;var o,i,f={__proto__:null,default:e};if(null===e||"object"!=typeof e&&"function"!=typeof e)return f;if(o=t?n:r){if(o.has(e))return o.get(e);o.set(e,f);}for(const t in e)"default"!==t&&{}.hasOwnProperty.call(e,t)&&((i=(o=Object.defineProperty)&&Object.getOwnPropertyDescriptor(e,t))&&(i.get||i.set)?o(f,t,i):f[t]=e[t]);return f;})(e,t);}(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f();}else if(typeof define==="function"&&define.amd){define([],f);}else{var g;if(typeof window!=="undefined"){g=window;}else if(typeof global!=="undefined"){g=global;}else if(typeof self!=="undefined"){g=self;}else{g=this;}g.retoldRemote=f();}})(function(){var define,module,exports;return function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a;}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r);},p,p.exports,r,e,n,t);}return n[i].exports;}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o;}return r;}()({1:[function(require,module,exports){},{}],2:[function(require,module,exports){arguments[4][1][0].apply(exports,arguments);},{"dup":1}],3:[function(require,module,exports){'use strict';var bind=require('function-bind');var $apply=require('./functionApply');var $call=require('./functionCall');var $reflectApply=require('./reflectApply');/** @type {import('./actualApply')} */module.exports=$reflectApply||bind.call($call,$apply);},{"./functionApply":4,"./functionCall":5,"./reflectApply":7,"function-bind":22}],4:[function(require,module,exports){'use strict';/** @type {import('./functionApply')} */module.exports=Function.prototype.apply;},{}],5:[function(require,module,exports){'use strict';/** @type {import('./functionCall')} */module.exports=Function.prototype.call;},{}],6:[function(require,module,exports){'use strict';var bind=require('function-bind');var $TypeError=require('es-errors/type');var $call=require('./functionCall');var $actualApply=require('./actualApply');/** @type {(args: [Function, thisArg?: unknown, ...args: unknown[]]) => Function} TODO FIXME, find a way to use import('.') */module.exports=function callBindBasic(args){if(args.length<1||typeof args[0]!=='function'){throw new $TypeError('a function is required');}return $actualApply(bind,$call,args);};},{"./actualApply":3,"./functionCall":5,"es-errors/type":16,"function-bind":22}],7:[function(require,module,exports){'use strict';/** @type {import('./reflectApply')} */module.exports=typeof Reflect!=='undefined'&&Reflect&&Reflect.apply;},{}],8:[function(require,module,exports){'use strict';var GetIntrinsic=require('get-intrinsic');var callBindBasic=require('call-bind-apply-helpers');/** @type {(thisArg: string, searchString: string, position?: number) => number} */var $indexOf=callBindBasic([GetIntrinsic('%String.prototype.indexOf%')]);/** @type {import('.')} */module.exports=function callBoundIntrinsic(name,allowMissing){/* eslint no-extra-parens: 0 */var intrinsic=/** @type {(this: unknown, ...args: unknown[]) => unknown} */GetIntrinsic(name,!!allowMissing);if(typeof intrinsic==='function'&&$indexOf(name,'.prototype.')>-1){return callBindBasic(/** @type {const} */[intrinsic]);}return intrinsic;};},{"call-bind-apply-helpers":6,"get-intrinsic":23}],9:[function(require,module,exports){'use strict';var callBind=require('call-bind-apply-helpers');var gOPD=require('gopd');var hasProtoAccessor;try{// eslint-disable-next-line no-extra-parens, no-proto
2
2
  hasProtoAccessor=/** @type {{ __proto__?: typeof Array.prototype }} */[].__proto__===Array.prototype;}catch(e){if(!e||typeof e!=='object'||!('code'in e)||e.code!=='ERR_PROTO_ACCESS'){throw e;}}// eslint-disable-next-line no-extra-parens
3
3
  var desc=!!hasProtoAccessor&&gOPD&&gOPD(Object.prototype,/** @type {keyof typeof Object.prototype} */'__proto__');var $Object=Object;var $getPrototypeOf=$Object.getPrototypeOf;/** @type {import('./get')} */module.exports=desc&&typeof desc.get==='function'?callBind([desc.get]):typeof $getPrototypeOf==='function'?/** @type {import('./get')} */function getDunder(value){// eslint-disable-next-line eqeqeq
4
4
  return $getPrototypeOf(value==null?value:$Object(value));}:false;},{"call-bind-apply-helpers":6,"gopd":28}],10:[function(require,module,exports){'use strict';/** @type {import('.')} */var $defineProperty=Object.defineProperty||false;if($defineProperty){try{$defineProperty({},'a',{value:1});}catch(e){// IE 8 has a broken defineProperty
@@ -9553,7 +9553,7 @@ if(psychotic){result.hostname=isAbsolute?'':srcPath.length?srcPath.shift():'';re
9553
9553
  if(result.pathname!==null||result.search!==null){result.path=(result.pathname?result.pathname:'')+(result.search?result.search:'');}result.auth=relative.auth||result.auth;result.slashes=result.slashes||relative.slashes;result.href=result.format();return result;};Url.prototype.parseHost=function(){var host=this.host;var port=portPattern.exec(host);if(port){port=port[0];if(port!==':'){this.port=port.substr(1);}host=host.substr(0,host.length-port.length);}if(host){this.hostname=host;}};exports.parse=urlParse;exports.resolve=urlResolve;exports.resolveObject=urlResolveObject;exports.format=urlFormat;exports.Url=Url;},{"punycode/":90,"qs":92}],114:[function(require,module,exports){module.exports={"Name":"Retold Remote","MainViewportViewIdentifier":"ContentEditor-Layout","AutoSolveAfterInitialize":true,"AutoRenderMainViewportViewAfterInitialize":false,"AutoRenderViewsAfterInitialize":false};},{}],115:[function(require,module,exports){const libContentEditorApplication=require('retold-content-system').PictContentEditor;const libPictSectionFileBrowser=require('pict-section-filebrowser');// Providers
9554
9554
  const libProviderRetoldRemote=require('./providers/Pict-Provider-RetoldRemote.js');const libProviderGalleryNavigation=require('./providers/Pict-Provider-GalleryNavigation.js');const libProviderGalleryFilterSort=require('./providers/Pict-Provider-GalleryFilterSort.js');const libProviderRetoldRemoteIcons=require('./providers/Pict-Provider-RetoldRemoteIcons.js');const libProviderRetoldRemoteTheme=require('./providers/Pict-Provider-RetoldRemoteTheme.js');const libProviderFormattingUtilities=require('./providers/Pict-Provider-FormattingUtilities.js');const libProviderToastNotification=require('./providers/Pict-Provider-ToastNotification.js');const libProviderCollectionManager=require('./providers/Pict-Provider-CollectionManager.js');const libProviderAISortManager=require('./providers/Pict-Provider-AISortManager.js');const libExtensionMaps=require('./RetoldRemote-ExtensionMaps.js');// Views (replace parent views)
9555
9555
  const libViewLayout=require('./views/PictView-Remote-Layout.js');const libViewTopBar=require('./views/PictView-Remote-TopBar.js');const libViewSettingsPanel=require('./views/PictView-Remote-SettingsPanel.js');// Views (new)
9556
- const libViewGallery=require('./views/PictView-Remote-Gallery.js');const libViewMediaViewer=require('./views/PictView-Remote-MediaViewer.js');const libViewImageViewer=require('./views/PictView-Remote-ImageViewer.js');const libViewVideoExplorer=require('./views/PictView-Remote-VideoExplorer.js');const libViewAudioExplorer=require('./views/PictView-Remote-AudioExplorer.js');const libViewImageExplorer=require('./views/PictView-Remote-ImageExplorer.js');const libViewVLCSetup=require('./views/PictView-Remote-VLCSetup.js');const libViewCollectionsPanel=require('./views/PictView-Remote-CollectionsPanel.js');const libViewFileInfoPanel=require('./views/PictView-Remote-FileInfoPanel.js');// Application configuration
9556
+ const libViewGallery=require('./views/PictView-Remote-Gallery.js');const libViewMediaViewer=require('./views/PictView-Remote-MediaViewer.js');const libViewImageViewer=require('./views/PictView-Remote-ImageViewer.js');const libViewVideoExplorer=require('./views/PictView-Remote-VideoExplorer.js');const libViewAudioExplorer=require('./views/PictView-Remote-AudioExplorer.js');const libViewImageExplorer=require('./views/PictView-Remote-ImageExplorer.js');const libViewVLCSetup=require('./views/PictView-Remote-VLCSetup.js');const libViewCollectionsPanel=require('./views/PictView-Remote-CollectionsPanel.js');const libViewFileInfoPanel=require('./views/PictView-Remote-FileInfoPanel.js');const libViewSubimagesPanel=require('./views/PictView-Remote-SubimagesPanel.js');// Application configuration
9557
9557
  const _DefaultConfiguration=require('./Pict-Application-RetoldRemote-Configuration.json');/**
9558
9558
  * Retold Remote Application
9559
9559
  *
@@ -9564,7 +9564,7 @@ const _DefaultConfiguration=require('./Pict-Application-RetoldRemote-Configurati
9564
9564
  */class RetoldRemoteApplication extends libContentEditorApplication{constructor(pFable,pOptions,pServiceHash){let tmpOptions=Object.assign({},_DefaultConfiguration,pOptions);super(pFable,tmpOptions,pServiceHash);// Replace parent views with media-focused versions.
9565
9565
  // Re-registering with the same ViewIdentifier replaces the parent's view.
9566
9566
  this.pict.addView('ContentEditor-Layout',libViewLayout.default_configuration,libViewLayout);this.pict.addView('ContentEditor-TopBar',libViewTopBar.default_configuration,libViewTopBar);// Add new views
9567
- this.pict.addView('RetoldRemote-Gallery',libViewGallery.default_configuration,libViewGallery);this.pict.addView('RetoldRemote-MediaViewer',libViewMediaViewer.default_configuration,libViewMediaViewer);this.pict.addView('RetoldRemote-ImageViewer',libViewImageViewer.default_configuration,libViewImageViewer);this.pict.addView('RetoldRemote-SettingsPanel',libViewSettingsPanel.default_configuration,libViewSettingsPanel);this.pict.addView('RetoldRemote-VideoExplorer',libViewVideoExplorer.default_configuration,libViewVideoExplorer);this.pict.addView('RetoldRemote-AudioExplorer',libViewAudioExplorer.default_configuration,libViewAudioExplorer);this.pict.addView('RetoldRemote-ImageExplorer',libViewImageExplorer.default_configuration,libViewImageExplorer);this.pict.addView('RetoldRemote-VLCSetup',libViewVLCSetup.default_configuration,libViewVLCSetup);this.pict.addView('RetoldRemote-CollectionsPanel',libViewCollectionsPanel.default_configuration,libViewCollectionsPanel);this.pict.addView('RetoldRemote-FileInfoPanel',libViewFileInfoPanel.default_configuration,libViewFileInfoPanel);// Add new providers
9567
+ this.pict.addView('RetoldRemote-Gallery',libViewGallery.default_configuration,libViewGallery);this.pict.addView('RetoldRemote-MediaViewer',libViewMediaViewer.default_configuration,libViewMediaViewer);this.pict.addView('RetoldRemote-ImageViewer',libViewImageViewer.default_configuration,libViewImageViewer);this.pict.addView('RetoldRemote-SettingsPanel',libViewSettingsPanel.default_configuration,libViewSettingsPanel);this.pict.addView('RetoldRemote-VideoExplorer',libViewVideoExplorer.default_configuration,libViewVideoExplorer);this.pict.addView('RetoldRemote-AudioExplorer',libViewAudioExplorer.default_configuration,libViewAudioExplorer);this.pict.addView('RetoldRemote-ImageExplorer',libViewImageExplorer.default_configuration,libViewImageExplorer);this.pict.addView('RetoldRemote-VLCSetup',libViewVLCSetup.default_configuration,libViewVLCSetup);this.pict.addView('RetoldRemote-CollectionsPanel',libViewCollectionsPanel.default_configuration,libViewCollectionsPanel);this.pict.addView('RetoldRemote-FileInfoPanel',libViewFileInfoPanel.default_configuration,libViewFileInfoPanel);this.pict.addView('RetoldRemote-SubimagesPanel',libViewSubimagesPanel.default_configuration,libViewSubimagesPanel);// Add new providers
9568
9568
  this.pict.addProvider('RetoldRemote-Provider',libProviderRetoldRemote.default_configuration,libProviderRetoldRemote);this.pict.addProvider('RetoldRemote-GalleryNavigation',libProviderGalleryNavigation.default_configuration,libProviderGalleryNavigation);this.pict.addProvider('RetoldRemote-GalleryFilterSort',libProviderGalleryFilterSort.default_configuration,libProviderGalleryFilterSort);this.pict.addProvider('RetoldRemote-Icons',libProviderRetoldRemoteIcons.default_configuration,libProviderRetoldRemoteIcons);this.pict.addProvider('RetoldRemote-Theme',libProviderRetoldRemoteTheme.default_configuration,libProviderRetoldRemoteTheme);this.pict.addProvider('RetoldRemote-FormattingUtilities',libProviderFormattingUtilities.default_configuration,libProviderFormattingUtilities);this.pict.addProvider('RetoldRemote-ToastNotification',libProviderToastNotification.default_configuration,libProviderToastNotification);this.pict.addProvider('RetoldRemote-CollectionManager',libProviderCollectionManager.default_configuration,libProviderCollectionManager);this.pict.addProvider('RetoldRemote-AISortManager',libProviderAISortManager.default_configuration,libProviderAISortManager);}onAfterInitializeAsync(fCallback){// Expose pict on window for inline onclick handlers
9569
9569
  if(typeof window!=='undefined'){window.pict=this.pict;}// Initialize RetoldRemote-specific state
9570
9570
  this.pict.AppData.RetoldRemote={ActiveMode:'gallery',// 'gallery' or 'viewer'
@@ -9700,7 +9700,7 @@ tmpDetailRows.parentElement.appendChild(tmpBtn);}/**
9700
9700
  }}/**
9701
9701
  * Load RetoldRemote settings from localStorage.
9702
9702
  */_loadRemoteSettings(){try{let tmpStored=localStorage.getItem('retold-remote-settings');if(tmpStored){let tmpSettings=JSON.parse(tmpStored);let tmpRemote=this.pict.AppData.RetoldRemote;if(tmpSettings.Theme)tmpRemote.Theme=tmpSettings.Theme;if(tmpSettings.ViewMode)tmpRemote.ViewMode=tmpSettings.ViewMode;if(tmpSettings.ThumbnailSize)tmpRemote.ThumbnailSize=tmpSettings.ThumbnailSize;if(tmpSettings.GalleryFilter){tmpRemote.GalleryFilter=tmpSettings.GalleryFilter;tmpRemote.FilterState.MediaType=tmpSettings.GalleryFilter;}if(typeof tmpSettings.ShowHiddenFiles==='boolean')tmpRemote.ShowHiddenFiles=tmpSettings.ShowHiddenFiles;if(typeof tmpSettings.DistractionFreeShowNav==='boolean')tmpRemote.DistractionFreeShowNav=tmpSettings.DistractionFreeShowNav;if(tmpSettings.ImageFitMode)tmpRemote.ImageFitMode=tmpSettings.ImageFitMode;if(typeof tmpSettings.SidebarCollapsed==='boolean')tmpRemote.SidebarCollapsed=tmpSettings.SidebarCollapsed;if(tmpSettings.SidebarWidth)tmpRemote.SidebarWidth=tmpSettings.SidebarWidth;if(tmpSettings.SortField)tmpRemote.SortField=tmpSettings.SortField;if(tmpSettings.SortDirection)tmpRemote.SortDirection=tmpSettings.SortDirection;if(Array.isArray(tmpSettings.FilterPresets))tmpRemote.FilterPresets=tmpSettings.FilterPresets;if(typeof tmpSettings.FilterPanelOpen==='boolean')tmpRemote.FilterPanelOpen=tmpSettings.FilterPanelOpen;if(typeof tmpSettings.AutoplayVideo==='boolean')tmpRemote.AutoplayVideo=tmpSettings.AutoplayVideo;if(typeof tmpSettings.AutoplayAudio==='boolean')tmpRemote.AutoplayAudio=tmpSettings.AutoplayAudio;if(typeof tmpSettings.ListShowExtension==='boolean')tmpRemote.ListShowExtension=tmpSettings.ListShowExtension;if(typeof tmpSettings.ListShowSize==='boolean')tmpRemote.ListShowSize=tmpSettings.ListShowSize;if(typeof tmpSettings.ListShowDate==='boolean')tmpRemote.ListShowDate=tmpSettings.ListShowDate;if(typeof tmpSettings.CollectionsPanelOpen==='boolean')tmpRemote.CollectionsPanelOpen=tmpSettings.CollectionsPanelOpen;if(tmpSettings.CollectionsPanelWidth)tmpRemote.CollectionsPanelWidth=tmpSettings.CollectionsPanelWidth;if(tmpSettings.LastUsedCollectionGUID)tmpRemote.LastUsedCollectionGUID=tmpSettings.LastUsedCollectionGUID;if(tmpSettings.FavoritesGUID)tmpRemote.FavoritesGUID=tmpSettings.FavoritesGUID;if(tmpSettings.AISortSettings&&typeof tmpSettings.AISortSettings==='object'){if(tmpSettings.AISortSettings.AIEndpoint)tmpRemote.AISortSettings.AIEndpoint=tmpSettings.AISortSettings.AIEndpoint;if(tmpSettings.AISortSettings.AIModel)tmpRemote.AISortSettings.AIModel=tmpSettings.AISortSettings.AIModel;if(tmpSettings.AISortSettings.AIProvider)tmpRemote.AISortSettings.AIProvider=tmpSettings.AISortSettings.AIProvider;if(tmpSettings.AISortSettings.NamingTemplate)tmpRemote.AISortSettings.NamingTemplate=tmpSettings.AISortSettings.NamingTemplate;}}}catch(pError){// localStorage may not be available
9703
- }}}module.exports=RetoldRemoteApplication;},{"./Pict-Application-RetoldRemote-Configuration.json":114,"./RetoldRemote-ExtensionMaps.js":117,"./providers/Pict-Provider-AISortManager.js":121,"./providers/Pict-Provider-CollectionManager.js":122,"./providers/Pict-Provider-FormattingUtilities.js":123,"./providers/Pict-Provider-GalleryFilterSort.js":124,"./providers/Pict-Provider-GalleryNavigation.js":125,"./providers/Pict-Provider-RetoldRemote.js":126,"./providers/Pict-Provider-RetoldRemoteIcons.js":127,"./providers/Pict-Provider-RetoldRemoteTheme.js":128,"./providers/Pict-Provider-ToastNotification.js":129,"./views/PictView-Remote-AudioExplorer.js":139,"./views/PictView-Remote-CollectionsPanel.js":140,"./views/PictView-Remote-FileInfoPanel.js":141,"./views/PictView-Remote-Gallery.js":142,"./views/PictView-Remote-ImageExplorer.js":143,"./views/PictView-Remote-ImageViewer.js":144,"./views/PictView-Remote-Layout.js":145,"./views/PictView-Remote-MediaViewer.js":146,"./views/PictView-Remote-SettingsPanel.js":147,"./views/PictView-Remote-TopBar.js":148,"./views/PictView-Remote-VLCSetup.js":149,"./views/PictView-Remote-VideoExplorer.js":150,"pict-section-filebrowser":63,"retold-content-system":100}],116:[function(require,module,exports){/**
9703
+ }}}module.exports=RetoldRemoteApplication;},{"./Pict-Application-RetoldRemote-Configuration.json":114,"./RetoldRemote-ExtensionMaps.js":117,"./providers/Pict-Provider-AISortManager.js":121,"./providers/Pict-Provider-CollectionManager.js":122,"./providers/Pict-Provider-FormattingUtilities.js":123,"./providers/Pict-Provider-GalleryFilterSort.js":124,"./providers/Pict-Provider-GalleryNavigation.js":125,"./providers/Pict-Provider-RetoldRemote.js":126,"./providers/Pict-Provider-RetoldRemoteIcons.js":127,"./providers/Pict-Provider-RetoldRemoteTheme.js":128,"./providers/Pict-Provider-ToastNotification.js":129,"./views/PictView-Remote-AudioExplorer.js":140,"./views/PictView-Remote-CollectionsPanel.js":141,"./views/PictView-Remote-FileInfoPanel.js":142,"./views/PictView-Remote-Gallery.js":143,"./views/PictView-Remote-ImageExplorer.js":144,"./views/PictView-Remote-ImageViewer.js":145,"./views/PictView-Remote-Layout.js":146,"./views/PictView-Remote-MediaViewer.js":147,"./views/PictView-Remote-SettingsPanel.js":148,"./views/PictView-Remote-SubimagesPanel.js":149,"./views/PictView-Remote-TopBar.js":150,"./views/PictView-Remote-VLCSetup.js":151,"./views/PictView-Remote-VideoExplorer.js":152,"pict-section-filebrowser":63,"retold-content-system":100}],116:[function(require,module,exports){/**
9704
9704
  * Retold Remote -- Browser Bundle Entry
9705
9705
  *
9706
9706
  * Exports the RetoldRemote application class for browser consumption.
@@ -9732,7 +9732,7 @@ const RawImageExtensions={'nef':true,'nrw':true,// Nikon
9732
9732
  'dng':true,// Adobe DNG (Leica, DJI, etc.)
9733
9733
  'heic':true,'heif':true// Apple / MPEG-H (limited browser support)
9734
9734
  };const ImageExtensions={'png':true,'jpg':true,'jpeg':true,'gif':true,'webp':true,'svg':true,'bmp':true,'ico':true,'avif':true,'tiff':true,'tif':true,'heic':true,'heif':true};// Merge raw extensions into ImageExtensions so getCategory() returns 'image'
9735
- for(let tmpKey in RawImageExtensions){ImageExtensions[tmpKey]=true;}const VideoExtensions={'mp4':true,'webm':true,'mov':true,'mkv':true,'avi':true,'wmv':true,'flv':true,'m4v':true,'ogv':true,'mpg':true,'mpeg':true,'mpe':true,'mpv':true,'m2v':true,'ts':true,'mts':true,'m2ts':true,'vob':true,'3gp':true,'3g2':true,'f4v':true,'rm':true,'rmvb':true,'divx':true,'asf':true,'mxf':true,'dv':true,'nsv':true,'nuv':true,'y4m':true,'wtv':true,'swf':true,'dat':true};const AudioExtensions={'mp3':true,'wav':true,'ogg':true,'flac':true,'aac':true,'m4a':true,'wma':true,'oga':true};const DocumentExtensions={'pdf':true,'epub':true,'mobi':true,'doc':true,'docx':true};/**
9735
+ for(let tmpKey in RawImageExtensions){ImageExtensions[tmpKey]=true;}const VideoExtensions={'mp4':true,'webm':true,'mov':true,'mkv':true,'avi':true,'wmv':true,'flv':true,'m4v':true,'ogv':true,'mpg':true,'mpeg':true,'mpe':true,'mpv':true,'m2v':true,'ts':true,'mts':true,'m2ts':true,'vob':true,'3gp':true,'3g2':true,'f4v':true,'rm':true,'rmvb':true,'divx':true,'asf':true,'mxf':true,'dv':true,'nsv':true,'nuv':true,'y4m':true,'wtv':true,'swf':true,'dat':true};const AudioExtensions={'mp3':true,'wav':true,'ogg':true,'flac':true,'aac':true,'m4a':true,'wma':true,'oga':true};const DocumentExtensions={'pdf':true,'epub':true,'mobi':true,'doc':true,'docx':true,'rtf':true,'odt':true,'wpd':true,'wps':true,'pages':true,'odp':true,'ppt':true,'pptx':true,'ods':true,'xls':true,'xlsx':true};/**
9736
9736
  * Get the media category for a file extension.
9737
9737
  *
9738
9738
  * @param {string} pExtension - Extension with or without leading dot (e.g. '.png' or 'png')
@@ -9766,7 +9766,9 @@ for(let tmpKey in RawImageExtensions){ImageExtensions[tmpKey]=true;}const VideoE
9766
9766
  * @returns {boolean} true if the add was initiated
9767
9767
  */addCurrentFileToCollection:function addCurrentFileToCollection(pGUID){let tmpRemote=this._getRemote();let tmpTargetGUID=pGUID||tmpRemote.LastUsedCollectionGUID;if(!tmpTargetGUID){return false;}// If the video explorer is active, delegate to addVideoFrameToCollection
9768
9768
  if(tmpRemote.ActiveMode==='video-explorer'){return this.addVideoFrameToCollection(tmpTargetGUID);}// If the audio explorer is active, delegate to addAudioSnippetToCollection
9769
- if(tmpRemote.ActiveMode==='audio-explorer'){return this.addAudioSnippetToCollection(tmpTargetGUID);}let tmpCurrentItem=this._resolveCurrentItem();if(!tmpCurrentItem||!tmpCurrentItem.Path){return false;}let tmpFilePath=tmpCurrentItem.Path;// Check if the current item is a folder prompt for folder vs contents
9769
+ if(tmpRemote.ActiveMode==='audio-explorer'){return this.addAudioSnippetToCollection(tmpTargetGUID);}// If the image explorer is active with a selection, add as subimage
9770
+ if(tmpRemote.ActiveMode==='image-explorer'){let tmpIEX=this.pict.views['RetoldRemote-ImageExplorer'];if(tmpIEX){let tmpActiveSelection=tmpIEX.getActiveSelection();if(tmpActiveSelection){return this.addSubimageToCollection(tmpTargetGUID,tmpActiveSelection,tmpIEX._currentPath);}}}// If viewing a document (PDF/EPUB) with a pending region, add as document-region
9771
+ if(tmpRemote.ActiveMode==='viewer'&&tmpRemote.CurrentViewerMediaType==='document'){let tmpMediaViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpMediaViewer&&tmpMediaViewer._pendingDocumentRegion){return this.addDocumentRegionToCollection(tmpTargetGUID,tmpMediaViewer._pendingDocumentRegion,tmpRemote.CurrentViewerFile);}}let tmpCurrentItem=this._resolveCurrentItem();if(!tmpCurrentItem||!tmpCurrentItem.Path){return false;}let tmpFilePath=tmpCurrentItem.Path;// Check if the current item is a folder — prompt for folder vs contents
9770
9772
  if(tmpCurrentItem.Type==='folder'||tmpCurrentItem.Type==='archive'){let tmpSelf=this;this.showFolderChoicePrompt(pChoice=>{let tmpFolderItem={Type:pChoice==='contents'?'folder-contents':'folder',Path:tmpFilePath,Label:'',Note:''};tmpSelf.addItemsToCollection(tmpTargetGUID,[tmpFolderItem]);});return true;}// Build the item — detect archive subfiles and video timestamp context
9771
9773
  let tmpItem={Type:'file',Path:tmpFilePath,Label:'',Note:''};// Detect archive subfile — path contains an archive extension followed by /
9772
9774
  let tmpArchiveMatch=tmpFilePath.match(/^(.*?\.(zip|7z|rar|tar|tgz|cbz|cbr|tar\.gz|tar\.bz2|tar\.xz))\/(.*)/i);if(tmpArchiveMatch){tmpItem.Type='subfile';tmpItem.ArchivePath=tmpArchiveMatch[1];}// If we're viewing a video with the player active, capture current timestamp as video-frame
@@ -9808,6 +9810,24 @@ let tmpStart=0;let tmpEnd=tmpDuration;if(tmpAEX._selectionStart>=0&&tmpAEX._sele
9808
9810
  * @returns {boolean} true if the add was initiated
9809
9811
  */addAudioClipToCollection:function addAudioClipToCollection(pGUID,pStartTime,pEndTime){let tmpRemote=this._getRemote();let tmpTargetGUID=pGUID||tmpRemote.LastUsedCollectionGUID;if(!tmpTargetGUID){return false;}// Resolve file path: check audio explorer first, then fall back to viewer file
9810
9812
  let tmpFilePath=null;if(tmpRemote.ActiveMode==='audio-explorer'){let tmpAEX=this.pict.views['RetoldRemote-AudioExplorer'];if(tmpAEX&&tmpAEX._currentPath){tmpFilePath=tmpAEX._currentPath;}}if(!tmpFilePath){tmpFilePath=tmpRemote.CurrentViewerFile;}if(!tmpFilePath){return false;}let tmpFileName=tmpFilePath.replace(/^.*\//,'');let tmpItem={Type:'audio-clip',Path:tmpFilePath,AudioStart:pStartTime,AudioEnd:pEndTime,Label:tmpFileName+': '+this._formatTimestamp(pStartTime)+' \u2013 '+this._formatTimestamp(pEndTime),Note:''};this.addItemsToCollection(tmpTargetGUID,[tmpItem]);return true;},/**
9813
+ * Add a subimage region (crop) to a collection.
9814
+ *
9815
+ * @param {string} pGUID - Collection GUID
9816
+ * @param {object} pRegion - { X, Y, Width, Height, Label?, ID? } in image pixels
9817
+ * @param {string} [pFilePath] - Explicit file path (defaults to current viewer file)
9818
+ * @returns {boolean} true if the add was initiated
9819
+ */addSubimageToCollection:function addSubimageToCollection(pGUID,pRegion,pFilePath){let tmpRemote=this._getRemote();let tmpTargetGUID=pGUID||tmpRemote.LastUsedCollectionGUID;if(!tmpTargetGUID||!pRegion){return false;}let tmpFilePath=pFilePath||tmpRemote.CurrentViewerFile;if(!tmpFilePath){return false;}let tmpFileName=tmpFilePath.replace(/^.*\//,'');let tmpLabel=pRegion.Label?pRegion.Label:tmpFileName+': '+pRegion.Width+'\u00d7'+pRegion.Height+' at '+pRegion.X+','+pRegion.Y;let tmpItem={Type:'image-crop',Path:tmpFilePath,CropRegion:{X:pRegion.X,Y:pRegion.Y,Width:pRegion.Width,Height:pRegion.Height},Label:tmpLabel,Note:''};// If we have a hash for this file, include it
9820
+ let tmpProvider=this.pict.providers['RetoldRemote-Provider'];if(tmpProvider){let tmpHash=tmpProvider.getHashForPath(tmpFilePath);if(tmpHash){tmpItem.Hash=tmpHash;}}this.addItemsToCollection(tmpTargetGUID,[tmpItem]);return true;},/**
9821
+ * Add a document region (text selection or visual area) to a collection.
9822
+ *
9823
+ * @param {string} pGUID - Collection GUID
9824
+ * @param {object} pRegion - Region object with Type, Label, and type-specific fields
9825
+ * @param {string} [pFilePath] - Explicit file path (defaults to current viewer file)
9826
+ * @returns {boolean} true if the add was initiated
9827
+ */addDocumentRegionToCollection:function addDocumentRegionToCollection(pGUID,pRegion,pFilePath){let tmpRemote=this._getRemote();let tmpTargetGUID=pGUID||tmpRemote.LastUsedCollectionGUID;if(!tmpTargetGUID||!pRegion){return false;}let tmpFilePath=pFilePath||tmpRemote.CurrentViewerFile;if(!tmpFilePath){return false;}let tmpFileName=tmpFilePath.replace(/^.*\//,'');let tmpIsText=pRegion.Type==='text-selection';// Build label
9828
+ let tmpLabel=pRegion.Label||'';if(!tmpLabel){if(tmpIsText&&pRegion.SelectedText){tmpLabel=pRegion.SelectedText.substring(0,50);if(pRegion.SelectedText.length>50)tmpLabel+='\u2026';}else{tmpLabel=tmpFileName;}if(pRegion.PageNumber){tmpLabel='p.'+pRegion.PageNumber+': '+tmpLabel;}}let tmpItem={Type:'document-region',Path:tmpFilePath,Label:tmpLabel,Note:'',DocumentRegionType:pRegion.Type||'visual-region',PageNumber:pRegion.PageNumber||null,CFI:pRegion.CFI||null,SelectedText:pRegion.SelectedText||null};// Include crop region for visual selections
9829
+ if(!tmpIsText&&pRegion.X!==undefined){tmpItem.CropRegion={X:pRegion.X,Y:pRegion.Y,Width:pRegion.Width,Height:pRegion.Height};}// File hash
9830
+ let tmpProvider=this.pict.providers['RetoldRemote-Provider'];if(tmpProvider){let tmpHash=tmpProvider.getHashForPath(tmpFilePath);if(tmpHash){tmpItem.Hash=tmpHash;}}this.addItemsToCollection(tmpTargetGUID,[tmpItem]);return true;},/**
9811
9831
  * Format a timestamp in seconds to a human-readable string.
9812
9832
  *
9813
9833
  * @param {number} pSeconds - Timestamp in seconds
@@ -10388,7 +10408,10 @@ let tmpGalleryView=this.pict.views['RetoldRemote-Gallery'];if(tmpGalleryView){tm
10388
10408
  *
10389
10409
  * @param {object} pItem - The collection item record
10390
10410
  * @param {number} pItemIndex - Index within ActiveCollection.Items
10391
- */_navigateToCollectionItem(pItem,pItemIndex){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.BrowsingCollection=true;tmpRemote.BrowsingCollectionIndex=pItemIndex;if(pItem.Type==='video-clip'){let tmpVEX=this.pict.views['RetoldRemote-VideoExplorer'];if(tmpVEX){tmpVEX.showExplorer(pItem.Path,pItem.VideoStart,pItem.VideoEnd);}}else if(pItem.Type==='audio-clip'){let tmpAEX=this.pict.views['RetoldRemote-AudioExplorer'];if(tmpAEX){tmpAEX.showExplorer(pItem.Path,pItem.AudioStart,pItem.AudioEnd);}}else if(pItem.Type==='video-frame'&&pItem.FrameCacheKey&&pItem.FrameFilename){// Show the cached frame image directly in the viewer
10411
+ */_navigateToCollectionItem(pItem,pItemIndex){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.BrowsingCollection=true;tmpRemote.BrowsingCollectionIndex=pItemIndex;if(pItem.Type==='video-clip'){let tmpVEX=this.pict.views['RetoldRemote-VideoExplorer'];if(tmpVEX){tmpVEX.showExplorer(pItem.Path,pItem.VideoStart,pItem.VideoEnd);}}else if(pItem.Type==='audio-clip'){let tmpAEX=this.pict.views['RetoldRemote-AudioExplorer'];if(tmpAEX){tmpAEX.showExplorer(pItem.Path,pItem.AudioStart,pItem.AudioEnd);}}else if(pItem.Type==='document-region'){// Navigate to the document, then to the specific location
10412
+ let tmpApp=this.pict.PictApplication;if(tmpApp&&tmpApp.navigateToFile){tmpApp.navigateToFile(pItem.Path);// After the viewer loads, navigate to the specific page/CFI
10413
+ let tmpSelf=this;setTimeout(()=>{let tmpMediaViewer=tmpSelf.pict.views['RetoldRemote-MediaViewer'];if(tmpMediaViewer){if(pItem.CFI&&tmpMediaViewer._activeRendition){tmpMediaViewer._activeRendition.display(pItem.CFI);}else if(pItem.PageNumber&&typeof tmpMediaViewer._renderPdfPage==='function'){tmpMediaViewer._renderPdfPage(pItem.PageNumber);}}},1000);}}else if(pItem.Type==='image-crop'&&pItem.CropRegion){let tmpIEX=this.pict.views['RetoldRemote-ImageExplorer'];if(tmpIEX){tmpIEX.showExplorer(pItem.Path);// Zoom to the crop region after the viewer loads
10414
+ let tmpCrop=pItem.CropRegion;setTimeout(()=>{if(tmpIEX._osdViewer&&tmpIEX._dziData){let tmpImageRect=new OpenSeadragon.Rect(tmpCrop.X,tmpCrop.Y,tmpCrop.Width,tmpCrop.Height);let tmpViewportRect=tmpIEX._osdViewer.viewport.imageToViewportRectangle(tmpImageRect);tmpIEX._osdViewer.viewport.fitBounds(tmpViewportRect);}},800);}}else if(pItem.Type==='video-frame'&&pItem.FrameCacheKey&&pItem.FrameFilename){// Show the cached frame image directly in the viewer
10392
10415
  let tmpFrameURL='/api/media/video-frame/'+encodeURIComponent(pItem.FrameCacheKey)+'/'+encodeURIComponent(pItem.FrameFilename);let tmpViewer=this.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.showDirectImage(tmpFrameURL,pItem.Label||pItem.FrameFilename,pItem.Path);}}else{let tmpApp=this.pict.PictApplication;if(tmpApp&&tmpApp.navigateToFile){tmpApp.navigateToFile(pItem.Path);}}}/**
10393
10416
  * Navigate to the next file in the gallery list.
10394
10417
  * When browsing a collection, navigates through collection items instead.
@@ -10800,7 +10823,7 @@ let tmpTopBar=pGalleryNav.pict.views['ContentEditor-TopBar'];if(tmpTopBar&&typeo
10800
10823
  *
10801
10824
  * @param {GalleryNavigationProvider} pGalleryNav - The provider instance
10802
10825
  * @param {KeyboardEvent} pEvent - The keyboard event
10803
- */function handleImageExplorerKey(pGalleryNav,pEvent){let tmpIEX=pGalleryNav.pict.views['RetoldRemote-ImageExplorer'];if(!tmpIEX){return;}switch(pEvent.key){case'Escape':pEvent.preventDefault();tmpIEX.goBack();break;case'+':case'=':pEvent.preventDefault();tmpIEX.zoomIn();break;case'-':case'_':pEvent.preventDefault();tmpIEX.zoomOut();break;case'0':pEvent.preventDefault();tmpIEX.zoomHome();break;case'a':pEvent.preventDefault();{let tmpCollMgr=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollMgr){let tmpQuickGUID=tmpCollMgr.getQuickAddTargetGUID();if(tmpQuickGUID){tmpCollMgr.addCurrentFileToCollection(tmpQuickGUID);}else{let tmpTopBar=pGalleryNav.pict.views['ContentEditor-TopBar'];if(tmpTopBar&&typeof tmpTopBar.showAddToCollectionDropdown==='function'){tmpTopBar.showAddToCollectionDropdown();}}}}break;case'b':pEvent.preventDefault();{let tmpCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollManager){tmpCollManager.togglePanel();}}break;case'h':pEvent.preventDefault();{let tmpFavCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpFavCollManager){tmpFavCollManager.toggleFavorite();}}break;}}module.exports=handleImageExplorerKey;},{}],134:[function(require,module,exports){/**
10826
+ */function handleImageExplorerKey(pGalleryNav,pEvent){let tmpIEX=pGalleryNav.pict.views['RetoldRemote-ImageExplorer'];if(!tmpIEX){return;}switch(pEvent.key){case'Escape':pEvent.preventDefault();tmpIEX.goBack();break;case'+':case'=':pEvent.preventDefault();tmpIEX.zoomIn();break;case'-':case'_':pEvent.preventDefault();tmpIEX.zoomOut();break;case'0':pEvent.preventDefault();tmpIEX.zoomHome();break;case'a':pEvent.preventDefault();{let tmpCollMgr=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollMgr){let tmpQuickGUID=tmpCollMgr.getQuickAddTargetGUID();if(tmpQuickGUID){tmpCollMgr.addCurrentFileToCollection(tmpQuickGUID);}else{let tmpTopBar=pGalleryNav.pict.views['ContentEditor-TopBar'];if(tmpTopBar&&typeof tmpTopBar.showAddToCollectionDropdown==='function'){tmpTopBar.showAddToCollectionDropdown();}}}}break;case'b':pEvent.preventDefault();{let tmpCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollManager){tmpCollManager.togglePanel();}}break;case'h':pEvent.preventDefault();{let tmpFavCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpFavCollManager){tmpFavCollManager.toggleFavorite();}}break;case's':pEvent.preventDefault();tmpIEX.toggleSelectionMode();break;}}module.exports=handleImageExplorerKey;},{}],134:[function(require,module,exports){/**
10804
10827
  * Sidebar file list keyboard handler.
10805
10828
  *
10806
10829
  * @param {GalleryNavigationProvider} pGalleryNav - The provider instance
@@ -10824,7 +10847,8 @@ let tmpEndVEX=pGalleryNav.pict.views['RetoldRemote-VideoExplorer'];if(tmpEndVEX&
10824
10847
  * @param {GalleryNavigationProvider} pGalleryNav - The provider instance
10825
10848
  * @param {KeyboardEvent} pEvent - The keyboard event
10826
10849
  */function handleViewerKey(pGalleryNav,pEvent){let tmpRemote=pGalleryNav.pict.AppData.RetoldRemote;// Video action menu mode — intercept keys for menu options
10827
- if(tmpRemote.VideoMenuActive&&tmpRemote.CurrentViewerMediaType==='video'){switch(pEvent.key){case'Escape':pEvent.preventDefault();pGalleryNav.closeViewer();return;case'ArrowRight':case'j':pEvent.preventDefault();pGalleryNav.nextFile();return;case'ArrowLeft':case'k':pEvent.preventDefault();pGalleryNav.prevFile();return;case'e':pEvent.preventDefault();let tmpVEX=pGalleryNav.pict.views['RetoldRemote-VideoExplorer'];if(tmpVEX){tmpVEX.showExplorer(tmpRemote.CurrentViewerFile);}return;case' ':case'Enter':pEvent.preventDefault();let tmpViewer=pGalleryNav.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.playVideo();}return;case't':pEvent.preventDefault();let tmpMediaViewer=pGalleryNav.pict.views['RetoldRemote-MediaViewer'];if(tmpMediaViewer){tmpMediaViewer.loadVideoMenuFrame();}return;case'v':pEvent.preventDefault();pGalleryNav._streamWithVLC();return;case'a':pEvent.preventDefault();{let tmpMenuCollMgr=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpMenuCollMgr){let tmpMenuQuickGUID=tmpMenuCollMgr.getQuickAddTargetGUID();if(tmpMenuQuickGUID){tmpMenuCollMgr.addCurrentFileToCollection(tmpMenuQuickGUID);}else{let tmpMenuTopBar=pGalleryNav.pict.views['ContentEditor-TopBar'];if(tmpMenuTopBar&&typeof tmpMenuTopBar.showAddToCollectionDropdown==='function'){tmpMenuTopBar.showAddToCollectionDropdown();}}}}return;case'b':pEvent.preventDefault();{let tmpMenuCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpMenuCollManager){tmpMenuCollManager.togglePanel();}}return;case'h':pEvent.preventDefault();{let tmpMenuFavManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpMenuFavManager){tmpMenuFavManager.toggleFavorite();}}return;}return;}switch(pEvent.key){case'Escape':pEvent.preventDefault();pGalleryNav.closeViewer();break;case'ArrowRight':case'j':pEvent.preventDefault();pGalleryNav.nextFile();break;case'ArrowLeft':case'k':pEvent.preventDefault();pGalleryNav.prevFile();break;case'f':pEvent.preventDefault();pGalleryNav._toggleDistractionFree();break;case'i':pEvent.preventDefault();pGalleryNav._toggleFileInfo();break;case' ':pEvent.preventDefault();pGalleryNav._togglePlayPause();break;case'+':case'=':pEvent.preventDefault();pGalleryNav._zoomIn();break;case'-':pEvent.preventDefault();pGalleryNav._zoomOut();break;case'0':pEvent.preventDefault();pGalleryNav._zoomReset();break;case'z':pEvent.preventDefault();pGalleryNav._cycleFitMode();break;case'Enter':pEvent.preventDefault();pGalleryNav._streamWithVLC();break;case'v':pEvent.preventDefault();pGalleryNav._streamWithVLC();break;case'a':pEvent.preventDefault();{let tmpCollMgr=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollMgr){let tmpQuickGUID=tmpCollMgr.getQuickAddTargetGUID();if(tmpQuickGUID){// Quick-add the currently viewed file
10850
+ if(tmpRemote.VideoMenuActive&&tmpRemote.CurrentViewerMediaType==='video'){switch(pEvent.key){case'Escape':pEvent.preventDefault();pGalleryNav.closeViewer();return;case'ArrowRight':case'j':pEvent.preventDefault();pGalleryNav.nextFile();return;case'ArrowLeft':case'k':pEvent.preventDefault();pGalleryNav.prevFile();return;case'e':pEvent.preventDefault();let tmpVEX=pGalleryNav.pict.views['RetoldRemote-VideoExplorer'];if(tmpVEX){tmpVEX.showExplorer(tmpRemote.CurrentViewerFile);}return;case' ':case'Enter':pEvent.preventDefault();let tmpViewer=pGalleryNav.pict.views['RetoldRemote-MediaViewer'];if(tmpViewer){tmpViewer.playVideo();}return;case't':pEvent.preventDefault();let tmpMediaViewer=pGalleryNav.pict.views['RetoldRemote-MediaViewer'];if(tmpMediaViewer){tmpMediaViewer.loadVideoMenuFrame();}return;case'v':pEvent.preventDefault();pGalleryNav._streamWithVLC();return;case'a':pEvent.preventDefault();{let tmpMenuCollMgr=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpMenuCollMgr){let tmpMenuQuickGUID=tmpMenuCollMgr.getQuickAddTargetGUID();if(tmpMenuQuickGUID){tmpMenuCollMgr.addCurrentFileToCollection(tmpMenuQuickGUID);}else{let tmpMenuTopBar=pGalleryNav.pict.views['ContentEditor-TopBar'];if(tmpMenuTopBar&&typeof tmpMenuTopBar.showAddToCollectionDropdown==='function'){tmpMenuTopBar.showAddToCollectionDropdown();}}}}return;case'b':pEvent.preventDefault();{let tmpMenuCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpMenuCollManager){tmpMenuCollManager.togglePanel();}}return;case'h':pEvent.preventDefault();{let tmpMenuFavManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpMenuFavManager){tmpMenuFavManager.toggleFavorite();}}return;}return;}switch(pEvent.key){case'Escape':pEvent.preventDefault();pGalleryNav.closeViewer();break;case'ArrowRight':case'j':pEvent.preventDefault();pGalleryNav.nextFile();break;case'ArrowLeft':case'k':pEvent.preventDefault();pGalleryNav.prevFile();break;case'f':pEvent.preventDefault();pGalleryNav._toggleDistractionFree();break;case'i':pEvent.preventDefault();pGalleryNav._toggleFileInfo();break;case' ':pEvent.preventDefault();pGalleryNav._togglePlayPause();break;case'+':case'=':pEvent.preventDefault();pGalleryNav._zoomIn();break;case'-':pEvent.preventDefault();pGalleryNav._zoomOut();break;case'0':pEvent.preventDefault();pGalleryNav._zoomReset();break;case'z':pEvent.preventDefault();pGalleryNav._cycleFitMode();break;case's':pEvent.preventDefault();{let tmpMediaViewer=pGalleryNav.pict.views['RetoldRemote-MediaViewer'];if(tmpMediaViewer){let tmpViewerMediaType=tmpRemote.CurrentViewerMediaType;if(tmpViewerMediaType==='document'){// Toggle region selection for EPUB or PDF
10851
+ if(typeof tmpMediaViewer.ebookToggleRegionSelect==='function'&&tmpMediaViewer._activeRendition){tmpMediaViewer.ebookToggleRegionSelect();}else if(typeof tmpMediaViewer.pdfToggleRegionSelect==='function'&&tmpMediaViewer._pdfDocument){tmpMediaViewer.pdfToggleRegionSelect();}}}}break;case'Enter':pEvent.preventDefault();pGalleryNav._streamWithVLC();break;case'v':pEvent.preventDefault();pGalleryNav._streamWithVLC();break;case'a':pEvent.preventDefault();{let tmpCollMgr=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollMgr){let tmpQuickGUID=tmpCollMgr.getQuickAddTargetGUID();if(tmpQuickGUID){// Quick-add the currently viewed file
10828
10852
  tmpCollMgr.addCurrentFileToCollection(tmpQuickGUID);}else{// No active or last-used collection — open the picker
10829
10853
  let tmpTopBar=pGalleryNav.pict.views['ContentEditor-TopBar'];if(tmpTopBar&&typeof tmpTopBar.showAddToCollectionDropdown==='function'){tmpTopBar.showAddToCollectionDropdown();}}}}break;case'b':pEvent.preventDefault();{let tmpCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollManager){tmpCollManager.togglePanel();}}break;case'd':pEvent.preventDefault();pGalleryNav._toggleDistractionFree();break;case'e':pEvent.preventDefault();{let tmpMediaType=tmpRemote.CurrentViewerMediaType;if(tmpMediaType==='video'){let tmpVEX=pGalleryNav.pict.views['RetoldRemote-VideoExplorer'];if(tmpVEX){tmpVEX.showExplorer(tmpRemote.CurrentViewerFile);}}else if(tmpMediaType==='audio'){let tmpAEX=pGalleryNav.pict.views['RetoldRemote-AudioExplorer'];if(tmpAEX){tmpAEX.showExplorer(tmpRemote.CurrentViewerFile);}}else if(tmpMediaType==='image'){let tmpIEX=pGalleryNav.pict.views['RetoldRemote-ImageExplorer'];if(tmpIEX){tmpIEX.showExplorer(tmpRemote.CurrentViewerFile);}}}break;case'1':pEvent.preventDefault();pGalleryNav.switchViewerType('image');break;case'2':pEvent.preventDefault();pGalleryNav.switchViewerType('video');break;case'3':pEvent.preventDefault();pGalleryNav.switchViewerType('audio');break;case'4':pEvent.preventDefault();pGalleryNav.switchViewerType('text');break;case'h':pEvent.preventDefault();{let tmpFavCollManager=pGalleryNav.pict.providers['RetoldRemote-CollectionManager'];if(tmpFavCollManager){tmpFavCollManager.toggleFavorite();}}break;}}module.exports=handleViewerKey;},{}],137:[function(require,module,exports){/**
10830
10854
  * MediaViewer — Code Viewer Mixin
@@ -10862,7 +10886,8 @@ let tmpLineCount=pText.split('\n').length;let tmpLineHTML='';for(let i=1;i<=tmpL
10862
10886
  * MediaViewer — Ebook Viewer Mixin
10863
10887
  *
10864
10888
  * EPUB/MOBI rendering using epub.js, table of contents,
10865
- * page navigation, and MOBI-to-EPUB server-side conversion.
10889
+ * page navigation, text selection capture, visual rectangle
10890
+ * selection, and MOBI-to-EPUB server-side conversion.
10866
10891
  *
10867
10892
  * Mixed into RetoldRemoteMediaViewerView.prototype via Object.assign().
10868
10893
  * All methods access state through `this` (the view instance).
@@ -10870,7 +10895,7 @@ let tmpLineCount=pText.split('\n').length;let tmpLineHTML='';for(let i=1;i<=tmpL
10870
10895
  * @license MIT
10871
10896
  */module.exports={/**
10872
10897
  * Build the HTML shell for the ebook reader.
10873
- */_buildEbookHTML:function _buildEbookHTML(pURL,pFileName,pFilePath){return'<div class="retold-remote-ebook-wrap">'+'<div class="retold-remote-ebook-toc collapsed" id="RetoldRemote-EbookTOC">'+'<div class="retold-remote-ebook-toc-header">'+'<span>Contents</span>'+'<button class="retold-remote-ebook-toc-close" onclick="pict.views[\'RetoldRemote-MediaViewer\'].toggleEbookTOC()" title="Close">&times;</button>'+'</div>'+'<div class="retold-remote-ebook-toc-items" id="RetoldRemote-EbookTOCItems"></div>'+'</div>'+'<div class="retold-remote-ebook-reader">'+'<div class="retold-remote-ebook-content" id="RetoldRemote-EbookContent">'+'<div class="retold-remote-ebook-loading">Loading ebook...</div>'+'</div>'+'<div class="retold-remote-ebook-controls">'+'<button class="retold-remote-ebook-toc-btn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].toggleEbookTOC()">&#9776; TOC</button>'+'<button class="retold-remote-ebook-page-btn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].ebookPrevPage()">&larr; Prev</button>'+'<button class="retold-remote-ebook-page-btn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].ebookNextPage()">Next &rarr;</button>'+'</div>'+'</div>'+'</div>';},/**
10898
+ */_buildEbookHTML:function _buildEbookHTML(pURL,pFileName,pFilePath){return'<div class="retold-remote-ebook-wrap">'+'<div class="retold-remote-ebook-toc collapsed" id="RetoldRemote-EbookTOC">'+'<div class="retold-remote-ebook-toc-header">'+'<span>Contents</span>'+'<button class="retold-remote-ebook-toc-close" onclick="pict.views[\'RetoldRemote-MediaViewer\'].toggleEbookTOC()" title="Close">&times;</button>'+'</div>'+'<div class="retold-remote-ebook-toc-items" id="RetoldRemote-EbookTOCItems"></div>'+'</div>'+'<div class="retold-remote-ebook-reader">'+'<div class="retold-remote-ebook-content" id="RetoldRemote-EbookContent">'+'<div class="retold-remote-ebook-loading">Loading ebook...</div>'+'</div>'+'<div class="retold-remote-ebook-controls">'+'<button class="retold-remote-ebook-toc-btn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].toggleEbookTOC()">&#9776; TOC</button>'+'<button class="retold-remote-ebook-page-btn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].ebookPrevPage()">&larr; Prev</button>'+'<button class="retold-remote-ebook-page-btn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].ebookNextPage()">Next &rarr;</button>'+'<span style="flex:1;"></span>'+'<button class="retold-remote-ebook-page-btn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].ebookSaveSelection()">&#128190; Save Selection</button>'+'<button class="retold-remote-ebook-page-btn" id="RetoldRemote-EbookRegionSelectBtn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].ebookToggleRegionSelect()">&#9986; Select Region</button>'+'</div>'+'<div class="retold-remote-ebook-controls" id="RetoldRemote-EbookLabelInput" style="display:none;">'+'<input type="text" id="RetoldRemote-EbookLabelField" placeholder="Label..." '+'style="flex:1; padding:4px 8px; background:var(--retold-bg-secondary, #2d2d2d); color:var(--retold-text-primary, #d4d4d4); border:1px solid var(--retold-border, #444); border-radius:4px;" '+'onkeydown="if(event.key===\'Enter\'){pict.views[\'RetoldRemote-MediaViewer\'].ebookSaveLabel();}">'+'<button class="retold-remote-ebook-page-btn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].ebookSaveLabel()">Save</button>'+'<button class="retold-remote-ebook-page-btn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].ebookCancelSelection()">Cancel</button>'+'</div>'+'</div>'+'</div>';},/**
10874
10899
  * Load and render an ebook using epub.js.
10875
10900
  * For EPUB files, fetch directly. For MOBI files, convert server-side first.
10876
10901
  *
@@ -10903,7 +10928,179 @@ tmpBook.loaded.navigation.then(pNav=>{tmpSelf._renderEbookTOC(pNav.toc);});}).ca
10903
10928
  * Go to the next page in the ebook.
10904
10929
  */ebookNextPage:function ebookNextPage(){if(this._activeRendition){this._activeRendition.next();}},/**
10905
10930
  * Toggle the table of contents sidebar.
10906
- */toggleEbookTOC:function toggleEbookTOC(){let tmpTocEl=document.getElementById('RetoldRemote-EbookTOC');if(tmpTocEl){tmpTocEl.classList.toggle('collapsed');}}};},{}],139:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-AudioExplorer",DefaultRenderable:"RetoldRemote-AudioExplorer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteAudioExplorerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._currentPath='';this._waveformData=null;this._peaks=[];// View state
10931
+ */toggleEbookTOC:function toggleEbookTOC(){let tmpTocEl=document.getElementById('RetoldRemote-EbookTOC');if(tmpTocEl){tmpTocEl.classList.toggle('collapsed');}},/**
10932
+ * Capture the current text selection from the epub.js rendition,
10933
+ * derive a CFI, and show the label input for saving.
10934
+ */ebookSaveSelection:function ebookSaveSelection(){let tmpSelf=this;if(!this._activeRendition){this.pict.providers['RetoldRemote-ToastNotification'].showToast('No ebook loaded.');return;}let tmpContents=this._activeRendition.getContents();if(!tmpContents||tmpContents.length<1){this.pict.providers['RetoldRemote-ToastNotification'].showToast('Unable to access ebook contents.');return;}let tmpDoc=tmpContents[0].document;let tmpSelection=tmpDoc.getSelection();let tmpSelectedText=tmpSelection?tmpSelection.toString():'';if(!tmpSelectedText||tmpSelectedText.trim().length===0){this.pict.providers['RetoldRemote-ToastNotification'].showToast('Select text first.');return;}// Derive the CFI from the selection range
10935
+ let tmpCFI='';try{let tmpRange=tmpSelection.getRangeAt(0);tmpCFI=tmpContents[0].cfiFromRange(tmpRange);}catch(pError){this.pict.log.warn('Could not derive CFI from selection: '+pError.message);}// Get current location for spine index
10936
+ let tmpLocation=this._activeRendition.currentLocation();let tmpSpineIndex=tmpLocation&&tmpLocation.start?tmpLocation.start.index:-1;// Try to find the chapter title from the TOC
10937
+ let tmpChapterTitle='';try{let tmpTocItems=document.querySelectorAll('#RetoldRemote-EbookTOCItems .retold-remote-ebook-toc-item');if(tmpTocItems.length>0&&tmpSpineIndex>=0){// Best effort: use the TOC item closest to the spine index
10938
+ let tmpTocIndex=Math.min(tmpSpineIndex,tmpTocItems.length-1);tmpChapterTitle=tmpTocItems[tmpTocIndex].textContent.trim();}}catch(pError){// Chapter title is best-effort; ignore errors
10939
+ }// Store pending selection data on the view instance
10940
+ this._pendingEbookSelection={Type:'text-selection',CFI:tmpCFI,SpineIndex:tmpSpineIndex,ChapterTitle:tmpChapterTitle,SelectedText:tmpSelectedText};// Show the label input
10941
+ let tmpLabelInput=document.getElementById('RetoldRemote-EbookLabelInput');if(tmpLabelInput){tmpLabelInput.style.display='';}let tmpLabelField=document.getElementById('RetoldRemote-EbookLabelField');if(tmpLabelField){tmpLabelField.value='';tmpLabelField.focus();}},/**
10942
+ * Toggle visual rectangle selection mode over the ebook content area.
10943
+ * When enabled, an overlay captures mouse events to draw a rectangle.
10944
+ */ebookToggleRegionSelect:function ebookToggleRegionSelect(){let tmpSelf=this;let tmpContentEl=document.getElementById('RetoldRemote-EbookContent');let tmpToggleBtn=document.getElementById('RetoldRemote-EbookRegionSelectBtn');if(!tmpContentEl){return;}// If overlay already exists, remove it (toggle off)
10945
+ let tmpExistingOverlay=document.getElementById('RetoldRemote-EbookRegionOverlay');if(tmpExistingOverlay){tmpExistingOverlay.remove();if(tmpToggleBtn){tmpToggleBtn.style.background='';}this._ebookRegionActive=false;return;}this._ebookRegionActive=true;if(tmpToggleBtn){tmpToggleBtn.style.background='var(--retold-accent, #569cd6)';}// Create a transparent overlay div
10946
+ let tmpOverlay=document.createElement('div');tmpOverlay.id='RetoldRemote-EbookRegionOverlay';tmpOverlay.style.cssText='position:absolute; top:0; left:0; width:100%; height:100%; '+'cursor:crosshair; z-index:100; user-select:none;';tmpContentEl.style.position='relative';tmpContentEl.appendChild(tmpOverlay);let tmpDrawing=false;let tmpStartX=0;let tmpStartY=0;let tmpRectEl=null;tmpOverlay.addEventListener('mousedown',function(pEvent){pEvent.preventDefault();pEvent.stopPropagation();// Remove any previous rectangle
10947
+ let tmpOldRect=document.getElementById('RetoldRemote-EbookRegionRect');if(tmpOldRect){tmpOldRect.remove();}let tmpBounds=tmpOverlay.getBoundingClientRect();tmpStartX=pEvent.clientX-tmpBounds.left;tmpStartY=pEvent.clientY-tmpBounds.top;tmpDrawing=true;tmpRectEl=document.createElement('div');tmpRectEl.id='RetoldRemote-EbookRegionRect';tmpRectEl.style.cssText='position:absolute; border:2px dashed var(--retold-accent, #569cd6); '+'background:rgba(86, 156, 214, 0.15); pointer-events:none;';tmpRectEl.style.left=tmpStartX+'px';tmpRectEl.style.top=tmpStartY+'px';tmpRectEl.style.width='0px';tmpRectEl.style.height='0px';tmpOverlay.appendChild(tmpRectEl);});tmpOverlay.addEventListener('mousemove',function(pEvent){if(!tmpDrawing||!tmpRectEl){return;}pEvent.preventDefault();let tmpBounds=tmpOverlay.getBoundingClientRect();let tmpCurrentX=pEvent.clientX-tmpBounds.left;let tmpCurrentY=pEvent.clientY-tmpBounds.top;let tmpLeft=Math.min(tmpStartX,tmpCurrentX);let tmpTop=Math.min(tmpStartY,tmpCurrentY);let tmpWidth=Math.abs(tmpCurrentX-tmpStartX);let tmpHeight=Math.abs(tmpCurrentY-tmpStartY);tmpRectEl.style.left=tmpLeft+'px';tmpRectEl.style.top=tmpTop+'px';tmpRectEl.style.width=tmpWidth+'px';tmpRectEl.style.height=tmpHeight+'px';});tmpOverlay.addEventListener('mouseup',function(pEvent){if(!tmpDrawing||!tmpRectEl){return;}pEvent.preventDefault();tmpDrawing=false;let tmpBounds=tmpOverlay.getBoundingClientRect();let tmpEndX=pEvent.clientX-tmpBounds.left;let tmpEndY=pEvent.clientY-tmpBounds.top;let tmpRegionLeft=Math.min(tmpStartX,tmpEndX);let tmpRegionTop=Math.min(tmpStartY,tmpEndY);let tmpRegionWidth=Math.abs(tmpEndX-tmpStartX);let tmpRegionHeight=Math.abs(tmpEndY-tmpStartY);// Ignore tiny accidental drags
10948
+ if(tmpRegionWidth<5||tmpRegionHeight<5){if(tmpRectEl){tmpRectEl.remove();tmpRectEl=null;}return;}let tmpViewportWidth=tmpOverlay.offsetWidth;let tmpViewportHeight=tmpOverlay.offsetHeight;// Best-effort text extraction from the rectangle area
10949
+ let tmpExtractedText='';try{let tmpContents=tmpSelf._activeRendition.getContents();if(tmpContents&&tmpContents.length>0){let tmpDoc=tmpContents[0].document;let tmpBody=tmpDoc.body;if(tmpBody){// Walk text nodes and check if any fall within the rectangle
10950
+ let tmpTreeWalker=tmpDoc.createTreeWalker(tmpBody,NodeFilter.SHOW_TEXT,null,false);let tmpTextParts=[];let tmpNode;while(tmpNode=tmpTreeWalker.nextNode()){if(!tmpNode.textContent||tmpNode.textContent.trim().length===0){continue;}let tmpRange=tmpDoc.createRange();tmpRange.selectNodeContents(tmpNode);let tmpRects=tmpRange.getClientRects();for(let i=0;i<tmpRects.length;i++){let tmpR=tmpRects[i];// Check overlap with the drawn rectangle
10951
+ if(tmpR.right>=tmpRegionLeft&&tmpR.left<=tmpRegionLeft+tmpRegionWidth&&tmpR.bottom>=tmpRegionTop&&tmpR.top<=tmpRegionTop+tmpRegionHeight){tmpTextParts.push(tmpNode.textContent.trim());break;}}}tmpExtractedText=tmpTextParts.join(' ');}}}catch(pError){// Text extraction from region is best-effort
10952
+ tmpSelf.pict.log.warn('Region text extraction failed: '+pError.message);}// Get current location for spine index
10953
+ let tmpLocation=tmpSelf._activeRendition.currentLocation();let tmpSpineIndex=tmpLocation&&tmpLocation.start?tmpLocation.start.index:-1;// Store pending selection data
10954
+ tmpSelf._pendingEbookSelection={Type:'visual-region',X:Math.round(tmpRegionLeft),Y:Math.round(tmpRegionTop),Width:Math.round(tmpRegionWidth),Height:Math.round(tmpRegionHeight),ViewportWidth:tmpViewportWidth,ViewportHeight:tmpViewportHeight,SpineIndex:tmpSpineIndex,SelectedText:tmpExtractedText};// Show the label input
10955
+ let tmpLabelInput=document.getElementById('RetoldRemote-EbookLabelInput');if(tmpLabelInput){tmpLabelInput.style.display='';}let tmpLabelField=document.getElementById('RetoldRemote-EbookLabelField');if(tmpLabelField){tmpLabelField.value='';tmpLabelField.focus();}});},/**
10956
+ * Cancel any in-progress selection or region, hide the label input,
10957
+ * and remove the region overlay if present.
10958
+ */ebookCancelSelection:function ebookCancelSelection(){// Hide the label input
10959
+ let tmpLabelInput=document.getElementById('RetoldRemote-EbookLabelInput');if(tmpLabelInput){tmpLabelInput.style.display='none';}// Clear the label field
10960
+ let tmpLabelField=document.getElementById('RetoldRemote-EbookLabelField');if(tmpLabelField){tmpLabelField.value='';}// Remove region overlay and rectangle if present
10961
+ let tmpRect=document.getElementById('RetoldRemote-EbookRegionRect');if(tmpRect){tmpRect.remove();}let tmpOverlay=document.getElementById('RetoldRemote-EbookRegionOverlay');if(tmpOverlay){tmpOverlay.remove();}// Reset toggle button style
10962
+ let tmpToggleBtn=document.getElementById('RetoldRemote-EbookRegionSelectBtn');if(tmpToggleBtn){tmpToggleBtn.style.background='';}this._ebookRegionActive=false;this._pendingEbookSelection=null;},/**
10963
+ * Save the pending selection with the label from the input field.
10964
+ * POSTs to /api/media/subimage-regions and updates the sidebar.
10965
+ */ebookSaveLabel:function ebookSaveLabel(){let tmpSelf=this;if(!this._pendingEbookSelection){this.pict.providers['RetoldRemote-ToastNotification'].showToast('No selection to save.');return;}let tmpLabelField=document.getElementById('RetoldRemote-EbookLabelField');let tmpLabelValue=tmpLabelField?tmpLabelField.value.trim():'';if(!tmpLabelValue){this.pict.providers['RetoldRemote-ToastNotification'].showToast('Enter a label for the selection.');return;}let tmpRegion=this._pendingEbookSelection;tmpRegion.Label=tmpLabelValue;let tmpPayload={Path:this.pict.AppData.RetoldRemote.CurrentViewerFile,Region:tmpRegion};fetch('/api/media/subimage-regions',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(tmpPayload)}).then(pResponse=>{if(!pResponse.ok){throw new Error('HTTP '+pResponse.status);}return pResponse.json();}).then(pData=>{tmpSelf.pict.providers['RetoldRemote-ToastNotification'].showToast('Selection saved: '+tmpLabelValue);// Clean up the selection state and UI
10966
+ tmpSelf.ebookCancelSelection();// Refresh the sidebar panel if a regions panel method exists
10967
+ if(typeof tmpSelf.refreshSubimageRegions==='function'){tmpSelf.refreshSubimageRegions();}}).catch(pError=>{tmpSelf.pict.providers['RetoldRemote-ToastNotification'].showToast('Failed to save selection: '+pError.message);tmpSelf.pict.log.error('Ebook selection save error: '+pError.message);});}};},{}],139:[function(_require,module,exports){/**
10968
+ * MediaViewer — PDF Viewer Mixin
10969
+ *
10970
+ * Full pdf.js canvas renderer with text layer, page navigation,
10971
+ * zoom controls, text selection saving, and visual region selection.
10972
+ *
10973
+ * Mixed into RetoldRemoteMediaViewerView.prototype via Object.assign().
10974
+ * All methods access state through `this` (the view instance).
10975
+ *
10976
+ * @license MIT
10977
+ */const _PDF_JS_CDN_URL='https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.9.155/pdf.min.mjs';const _PDF_JS_WORKER_CDN_URL='https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.9.155/pdf.worker.min.mjs';module.exports={/**
10978
+ * Build the HTML shell for the PDF viewer.
10979
+ *
10980
+ * @param {string} pURL - Content URL for the file
10981
+ * @param {string} pFileName - Display file name
10982
+ * @param {string} pFilePath - Relative file path
10983
+ * @returns {string} HTML string
10984
+ */_buildPdfHTML:function _buildPdfHTML(pURL,pFileName,pFilePath){let tmpViewRef="pict.views['RetoldRemote-MediaViewer']";let tmpHTML='<div class="retold-remote-pdf-viewer">';// Controls bar
10985
+ tmpHTML+='<div class="retold-remote-pdf-controls" id="RetoldRemote-PdfControls">';tmpHTML+='<button class="retold-remote-pdf-btn" onclick="'+tmpViewRef+'.pdfPrevPage()" title="Previous page">&larr; Prev</button>';tmpHTML+='<span class="retold-remote-pdf-page-info">';tmpHTML+='Page <input type="number" id="RetoldRemote-PdfPageInput" class="retold-remote-pdf-page-input" value="1" min="1" '+'onchange="'+tmpViewRef+'.pdfGoToPage(parseInt(this.value,10))" '+'onkeydown="if(event.key===\'Enter\'){'+tmpViewRef+'.pdfGoToPage(parseInt(this.value,10));event.preventDefault();}">';tmpHTML+=' of <span id="RetoldRemote-PdfPageCount">0</span>';tmpHTML+='</span>';tmpHTML+='<button class="retold-remote-pdf-btn" onclick="'+tmpViewRef+'.pdfNextPage()" title="Next page">Next &rarr;</button>';tmpHTML+='<span class="retold-remote-pdf-separator"></span>';tmpHTML+='<button class="retold-remote-pdf-btn" onclick="'+tmpViewRef+'.pdfSaveSelection()" title="Save selected text">&#128190; Save Selection</button>';tmpHTML+='<button class="retold-remote-pdf-btn" id="RetoldRemote-PdfRegionBtn" onclick="'+tmpViewRef+'.pdfToggleRegionSelect()" title="Select a visual region">&#9986; Select Region</button>';tmpHTML+='<span class="retold-remote-pdf-separator"></span>';tmpHTML+='<button class="retold-remote-pdf-btn" onclick="'+tmpViewRef+'.pdfZoomIn()" title="Zoom in (+)">+ Zoom In</button>';tmpHTML+='<span class="retold-remote-pdf-zoom-label" id="RetoldRemote-PdfZoomLabel">150%</span>';tmpHTML+='<button class="retold-remote-pdf-btn" onclick="'+tmpViewRef+'.pdfZoomOut()" title="Zoom out (-)">- Zoom Out</button>';tmpHTML+='<button class="retold-remote-pdf-btn" onclick="'+tmpViewRef+'.pdfZoomFit()" title="Fit to width">Fit</button>';tmpHTML+='</div>';// Content area
10986
+ tmpHTML+='<div class="retold-remote-pdf-content" id="RetoldRemote-PdfContent">';tmpHTML+='<div class="retold-remote-pdf-wrap" id="RetoldRemote-PdfWrap">';tmpHTML+='<canvas id="RetoldRemote-PdfCanvas"></canvas>';tmpHTML+='<div id="RetoldRemote-PdfTextLayer" class="retold-remote-pdf-text-layer"></div>';tmpHTML+='<div id="RetoldRemote-PdfSelectionOverlay" class="retold-remote-pdf-selection-overlay" style="display:none;"></div>';tmpHTML+='<div id="RetoldRemote-PdfRegionOverlays" class="retold-remote-pdf-region-overlays"></div>';tmpHTML+='</div>';tmpHTML+='<div class="retold-remote-pdf-loading" id="RetoldRemote-PdfLoading">Loading PDF...</div>';tmpHTML+='</div>';// Inline label input (hidden until needed)
10987
+ tmpHTML+='<div class="retold-remote-pdf-label-bar" id="RetoldRemote-PdfLabelInput" style="display:none;">';tmpHTML+='<input type="text" id="RetoldRemote-PdfLabelField" class="retold-remote-pdf-label-field" placeholder="Label this selection\u2026" '+'onkeydown="if(event.key===\'Enter\'){'+tmpViewRef+'.pdfSaveLabel();event.preventDefault();event.stopPropagation();}'+'if(event.key===\'Escape\'){'+tmpViewRef+'.pdfCancelSelection();event.preventDefault();event.stopPropagation();}">';tmpHTML+='<button class="retold-remote-pdf-btn" onclick="'+tmpViewRef+'.pdfSaveLabel()">Save</button>';tmpHTML+='<button class="retold-remote-pdf-btn" onclick="'+tmpViewRef+'.pdfCancelSelection()">Cancel</button>';tmpHTML+='</div>';tmpHTML+='</div>';// Inline styles for the PDF viewer
10988
+ tmpHTML+='<style>';tmpHTML+='.retold-remote-pdf-viewer { display: flex; flex-direction: column; height: 100%; overflow: hidden; }';tmpHTML+='.retold-remote-pdf-controls { display: flex; align-items: center; gap: 6px; padding: 6px 12px; background: var(--retold-bg-secondary, #252526); border-bottom: 1px solid var(--retold-border, #3e4451); flex-shrink: 0; flex-wrap: wrap; }';tmpHTML+='.retold-remote-pdf-btn { background: var(--retold-bg-tertiary, #2d2d2d); color: var(--retold-text-primary, #abb2bf); border: 1px solid var(--retold-border, #3e4451); border-radius: 4px; padding: 4px 10px; font-size: 0.78rem; cursor: pointer; white-space: nowrap; }';tmpHTML+='.retold-remote-pdf-btn:hover { background: var(--retold-bg-hover, #3e4451); }';tmpHTML+='.retold-remote-pdf-btn.active { background: var(--retold-accent, #569cd6); color: #fff; }';tmpHTML+='.retold-remote-pdf-page-info { font-size: 0.78rem; color: var(--retold-text-secondary, #8b949e); display: flex; align-items: center; gap: 4px; }';tmpHTML+='.retold-remote-pdf-page-input { width: 48px; background: var(--retold-bg-input, #1e1e1e); color: var(--retold-text-primary, #abb2bf); border: 1px solid var(--retold-border, #3e4451); border-radius: 4px; padding: 2px 6px; font-size: 0.78rem; text-align: center; }';tmpHTML+='.retold-remote-pdf-separator { width: 1px; height: 20px; background: var(--retold-border, #3e4451); margin: 0 4px; }';tmpHTML+='.retold-remote-pdf-zoom-label { font-size: 0.78rem; color: var(--retold-text-secondary, #8b949e); min-width: 40px; text-align: center; }';tmpHTML+='.retold-remote-pdf-content { flex: 1; overflow: auto; position: relative; background: var(--retold-bg-primary, #1e1e1e); }';tmpHTML+='.retold-remote-pdf-wrap { position: relative; display: inline-block; margin: 16px auto; }';tmpHTML+='.retold-remote-pdf-content { text-align: center; }';tmpHTML+='.retold-remote-pdf-text-layer { position: absolute; top: 0; left: 0; overflow: hidden; opacity: 0.25; line-height: 1.0; }';tmpHTML+='.retold-remote-pdf-text-layer > span { position: absolute; white-space: pre; color: transparent; }';tmpHTML+='.retold-remote-pdf-text-layer ::selection { background: rgba(86, 156, 214, 0.4); }';tmpHTML+='.retold-remote-pdf-selection-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; cursor: crosshair; z-index: 5; }';tmpHTML+='.retold-remote-pdf-region-overlays { position: absolute; top: 0; left: 0; pointer-events: none; }';tmpHTML+='.retold-remote-pdf-region-rect { position: absolute; border: 2px solid rgba(86, 156, 214, 0.8); background: rgba(86, 156, 214, 0.12); pointer-events: none; }';tmpHTML+='.retold-remote-pdf-region-label { position: absolute; bottom: -18px; left: 0; font-size: 0.65rem; color: var(--retold-accent, #569cd6); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 160px; pointer-events: none; }';tmpHTML+='.retold-remote-pdf-active-rect { position: absolute; border: 2px dashed rgba(214, 156, 86, 0.9); background: rgba(214, 156, 86, 0.15); pointer-events: none; }';tmpHTML+='.retold-remote-pdf-loading { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); font-size: 0.9rem; color: var(--retold-text-secondary, #8b949e); }';tmpHTML+='.retold-remote-pdf-label-bar { display: flex; align-items: center; gap: 6px; padding: 6px 12px; background: var(--retold-bg-secondary, #252526); border-top: 1px solid var(--retold-border, #3e4451); flex-shrink: 0; }';tmpHTML+='.retold-remote-pdf-label-field { flex: 1; background: var(--retold-bg-input, #1e1e1e); color: var(--retold-text-primary, #abb2bf); border: 1px solid var(--retold-border, #3e4451); border-radius: 4px; padding: 4px 10px; font-size: 0.78rem; }';tmpHTML+='</style>';return tmpHTML;},/**
10989
+ * Load the pdf.js library from CDN (if needed) and open a PDF document.
10990
+ *
10991
+ * @param {string} pContentURL - URL to fetch the PDF from
10992
+ * @param {string} pFilePath - Relative file path
10993
+ */_loadPdfViewer:function _loadPdfViewer(pContentURL,pFilePath){let tmpSelf=this;// Initialize instance state
10994
+ this._pdfDocument=null;this._pdfCurrentPage=1;this._pdfPageCount=0;this._pdfScale=1.5;this._pdfSelectionMode=false;this._pdfSavedRegions=[];this._pdfPendingRegion=null;this._pdfPendingText=null;this._pdfLibLoaded=false;this._pdfFilePath=pFilePath;this._ensurePdfJsLoaded(function(){tmpSelf._pdfLibLoaded=true;tmpSelf._openPdfDocument(pContentURL);});},/**
10995
+ * Ensure pdf.js is loaded from CDN via dynamic import.
10996
+ *
10997
+ * @param {Function} fCallback - Called when pdf.js is ready
10998
+ */_ensurePdfJsLoaded:function _ensurePdfJsLoaded(fCallback){if(typeof window!=='undefined'&&window.pdfjsLib){return fCallback();}let tmpSelf=this;let tmpLoadingEl=document.getElementById('RetoldRemote-PdfLoading');if(tmpLoadingEl){tmpLoadingEl.textContent='Loading PDF renderer...';}(specifier=>new Promise(r=>r(`${specifier}`)).then(s=>_interopRequireWildcard(require(s))))(_PDF_JS_CDN_URL).then(function(pModule){window.pdfjsLib=pModule;window.pdfjsLib.GlobalWorkerOptions.workerSrc=_PDF_JS_WORKER_CDN_URL;fCallback();}).catch(function(pError){let tmpEl=document.getElementById('RetoldRemote-PdfLoading');if(tmpEl){tmpEl.textContent='Failed to load PDF renderer: '+pError.message;}});},/**
10999
+ * Open a PDF document from a URL using pdf.js.
11000
+ *
11001
+ * @param {string} pContentURL - URL to fetch the PDF from
11002
+ */_openPdfDocument:function _openPdfDocument(pContentURL){let tmpSelf=this;let tmpLoadingEl=document.getElementById('RetoldRemote-PdfLoading');if(tmpLoadingEl){tmpLoadingEl.textContent='Opening PDF...';}let tmpLoadingTask=window.pdfjsLib.getDocument(pContentURL);tmpLoadingTask.promise.then(function(pDocument){tmpSelf._pdfDocument=pDocument;tmpSelf._pdfPageCount=pDocument.numPages;tmpSelf._pdfCurrentPage=1;// Update the page count display
11003
+ let tmpCountEl=document.getElementById('RetoldRemote-PdfPageCount');if(tmpCountEl){tmpCountEl.textContent=pDocument.numPages;}let tmpPageInput=document.getElementById('RetoldRemote-PdfPageInput');if(tmpPageInput){tmpPageInput.max=pDocument.numPages;}// Hide loading indicator
11004
+ if(tmpLoadingEl){tmpLoadingEl.style.display='none';}// Render the first page
11005
+ tmpSelf._renderPdfPage(1);// Load saved regions
11006
+ tmpSelf._pdfLoadSavedRegions(tmpSelf._pdfFilePath);}).catch(function(pError){if(tmpLoadingEl){tmpLoadingEl.textContent='Failed to open PDF: '+pError.message;}});},/**
11007
+ * Render a specific page of the PDF onto the canvas.
11008
+ *
11009
+ * @param {number} pPageNumber - 1-based page number
11010
+ */_renderPdfPage:function _renderPdfPage(pPageNumber){if(!this._pdfDocument){return;}if(pPageNumber<1||pPageNumber>this._pdfPageCount){return;}let tmpSelf=this;this._pdfCurrentPage=pPageNumber;this._pdfDocument.getPage(pPageNumber).then(function(pPage){let tmpViewport=pPage.getViewport({scale:tmpSelf._pdfScale});let tmpCanvas=document.getElementById('RetoldRemote-PdfCanvas');if(!tmpCanvas){return;}let tmpContext=tmpCanvas.getContext('2d');// Set canvas dimensions to match the viewport
11011
+ tmpCanvas.width=tmpViewport.width;tmpCanvas.height=tmpViewport.height;// Set the wrap container dimensions
11012
+ let tmpWrap=document.getElementById('RetoldRemote-PdfWrap');if(tmpWrap){tmpWrap.style.width=tmpViewport.width+'px';tmpWrap.style.height=tmpViewport.height+'px';}// Render the page onto the canvas
11013
+ let tmpRenderContext={canvasContext:tmpContext,viewport:tmpViewport};pPage.render(tmpRenderContext).promise.then(function(){// After canvas renders, build the text layer
11014
+ tmpSelf._renderPdfTextLayer(pPage,tmpViewport);// Render saved region overlays for this page
11015
+ tmpSelf._pdfRenderRegionOverlays();});// Update the page number input
11016
+ let tmpPageInput=document.getElementById('RetoldRemote-PdfPageInput');if(tmpPageInput){tmpPageInput.value=pPageNumber;}// Update the zoom label
11017
+ tmpSelf._pdfUpdateZoomLabel();}).catch(function(pError){let tmpLoadingEl=document.getElementById('RetoldRemote-PdfLoading');if(tmpLoadingEl){tmpLoadingEl.style.display='';tmpLoadingEl.textContent='Failed to render page: '+pError.message;}});},/**
11018
+ * Render the text layer on top of the canvas so text can be selected.
11019
+ *
11020
+ * @param {Object} pPage - pdf.js page object
11021
+ * @param {Object} pViewport - pdf.js viewport for the current scale
11022
+ */_renderPdfTextLayer:function _renderPdfTextLayer(pPage,pViewport){let tmpTextLayerEl=document.getElementById('RetoldRemote-PdfTextLayer');if(!tmpTextLayerEl){return;}// Clear previous text layer content
11023
+ tmpTextLayerEl.innerHTML='';// Match text layer dimensions to the canvas
11024
+ tmpTextLayerEl.style.width=pViewport.width+'px';tmpTextLayerEl.style.height=pViewport.height+'px';pPage.getTextContent().then(function(pTextContent){// Use pdf.js built-in renderTextLayer if available
11025
+ if(window.pdfjsLib&&window.pdfjsLib.renderTextLayer){let tmpTask=window.pdfjsLib.renderTextLayer({textContentSource:pTextContent,container:tmpTextLayerEl,viewport:pViewport});tmpTask.promise.catch(function(){// Silently ignore text layer errors
11026
+ });}else{// Manual fallback: position spans from the text content items
11027
+ let tmpItems=pTextContent.items;for(let i=0;i<tmpItems.length;i++){let tmpItem=tmpItems[i];if(!tmpItem.str){continue;}let tmpSpan=document.createElement('span');tmpSpan.textContent=tmpItem.str;// Position from the transform matrix [scaleX, skewX, skewY, scaleY, translateX, translateY]
11028
+ let tmpTx=tmpItem.transform;let tmpFontSize=Math.sqrt(tmpTx[2]*tmpTx[2]+tmpTx[3]*tmpTx[3]);let tmpLeft=tmpTx[4]*(pViewport.width/(pViewport.viewBox[2]-pViewport.viewBox[0]));// PDF coordinates have origin at bottom-left; canvas at top-left
11029
+ let tmpTop=pViewport.height-tmpTx[5]*(pViewport.height/(pViewport.viewBox[3]-pViewport.viewBox[1]));tmpSpan.style.left=tmpLeft+'px';tmpSpan.style.top=tmpTop-tmpFontSize+'px';tmpSpan.style.fontSize=tmpFontSize+'px';tmpSpan.style.fontFamily=tmpItem.fontName||'sans-serif';tmpTextLayerEl.appendChild(tmpSpan);}}});},/**
11030
+ * Update the zoom percentage label.
11031
+ */_pdfUpdateZoomLabel:function _pdfUpdateZoomLabel(){let tmpLabel=document.getElementById('RetoldRemote-PdfZoomLabel');if(tmpLabel){tmpLabel.textContent=Math.round(this._pdfScale*100)+'%';}},// -----------------------------------------------------------------
11032
+ // Page navigation
11033
+ // -----------------------------------------------------------------
11034
+ /**
11035
+ * Go to the next page.
11036
+ */pdfNextPage:function pdfNextPage(){if(this._pdfCurrentPage<this._pdfPageCount){this._renderPdfPage(this._pdfCurrentPage+1);}},/**
11037
+ * Go to the previous page.
11038
+ */pdfPrevPage:function pdfPrevPage(){if(this._pdfCurrentPage>1){this._renderPdfPage(this._pdfCurrentPage-1);}},/**
11039
+ * Jump to a specific page number.
11040
+ *
11041
+ * @param {number} pPageNumber - 1-based page number
11042
+ */pdfGoToPage:function pdfGoToPage(pPageNumber){if(isNaN(pPageNumber)||pPageNumber<1){pPageNumber=1;}if(pPageNumber>this._pdfPageCount){pPageNumber=this._pdfPageCount;}this._renderPdfPage(pPageNumber);},// -----------------------------------------------------------------
11043
+ // Zoom
11044
+ // -----------------------------------------------------------------
11045
+ /**
11046
+ * Zoom in by 25%.
11047
+ */pdfZoomIn:function pdfZoomIn(){this._pdfScale=Math.min(this._pdfScale+0.25,5.0);this._renderPdfPage(this._pdfCurrentPage);},/**
11048
+ * Zoom out by 25%.
11049
+ */pdfZoomOut:function pdfZoomOut(){this._pdfScale=Math.max(this._pdfScale-0.25,0.25);this._renderPdfPage(this._pdfCurrentPage);},/**
11050
+ * Fit the PDF page width to the content area.
11051
+ */pdfZoomFit:function pdfZoomFit(){if(!this._pdfDocument){return;}let tmpSelf=this;this._pdfDocument.getPage(this._pdfCurrentPage).then(function(pPage){let tmpContentEl=document.getElementById('RetoldRemote-PdfContent');if(!tmpContentEl){return;}// Get the page dimensions at scale 1.0
11052
+ let tmpBaseViewport=pPage.getViewport({scale:1.0});let tmpAvailableWidth=tmpContentEl.clientWidth-32;// Subtract padding
11053
+ let tmpFitScale=tmpAvailableWidth/tmpBaseViewport.width;tmpSelf._pdfScale=Math.max(0.25,Math.min(tmpFitScale,5.0));tmpSelf._renderPdfPage(tmpSelf._pdfCurrentPage);});},// -----------------------------------------------------------------
11054
+ // Text selection saving
11055
+ // -----------------------------------------------------------------
11056
+ /**
11057
+ * Save the currently selected text from the text layer.
11058
+ */pdfSaveSelection:function pdfSaveSelection(){let tmpSelectedText='';if(typeof window!=='undefined'&&window.getSelection){tmpSelectedText=window.getSelection().toString().trim();}if(!tmpSelectedText){let tmpToast=this.pict.providers['RetoldRemote-ToastNotification'];if(tmpToast){tmpToast.showToast('Select some text in the PDF first.');}return;}this._pdfPendingText=tmpSelectedText;this._pdfPendingRegion=null;// Show the label input bar
11059
+ let tmpLabelBar=document.getElementById('RetoldRemote-PdfLabelInput');if(tmpLabelBar){tmpLabelBar.style.display='';}let tmpField=document.getElementById('RetoldRemote-PdfLabelField');if(tmpField){tmpField.value='';tmpField.placeholder='Label this text selection\u2026';tmpField.focus();}},// -----------------------------------------------------------------
11060
+ // Visual region selection
11061
+ // -----------------------------------------------------------------
11062
+ /**
11063
+ * Toggle the visual region selection mode.
11064
+ */pdfToggleRegionSelect:function pdfToggleRegionSelect(){this._pdfSelectionMode=!this._pdfSelectionMode;let tmpOverlay=document.getElementById('RetoldRemote-PdfSelectionOverlay');let tmpBtn=document.getElementById('RetoldRemote-PdfRegionBtn');if(this._pdfSelectionMode){if(tmpOverlay){tmpOverlay.style.display='';}if(tmpBtn){tmpBtn.classList.add('active');}this._pdfSetupRegionDrag();}else{if(tmpOverlay){tmpOverlay.style.display='none';tmpOverlay.innerHTML='';}if(tmpBtn){tmpBtn.classList.remove('active');}this._pdfCleanupRegionDrag();}},/**
11065
+ * Set up mouse event handlers for dragging a selection rectangle.
11066
+ */_pdfSetupRegionDrag:function _pdfSetupRegionDrag(){let tmpSelf=this;let tmpOverlay=document.getElementById('RetoldRemote-PdfSelectionOverlay');if(!tmpOverlay){return;}let tmpDragging=false;let tmpStartX=0;let tmpStartY=0;let tmpRectEl=null;this._pdfRegionMouseDown=function(pEvent){pEvent.preventDefault();tmpDragging=true;let tmpRect=tmpOverlay.getBoundingClientRect();tmpStartX=pEvent.clientX-tmpRect.left;tmpStartY=pEvent.clientY-tmpRect.top;// Create the selection rectangle element
11067
+ tmpRectEl=document.createElement('div');tmpRectEl.className='retold-remote-pdf-active-rect';tmpRectEl.style.left=tmpStartX+'px';tmpRectEl.style.top=tmpStartY+'px';tmpRectEl.style.width='0px';tmpRectEl.style.height='0px';tmpOverlay.appendChild(tmpRectEl);};this._pdfRegionMouseMove=function(pEvent){if(!tmpDragging||!tmpRectEl){return;}let tmpRect=tmpOverlay.getBoundingClientRect();let tmpCurrentX=pEvent.clientX-tmpRect.left;let tmpCurrentY=pEvent.clientY-tmpRect.top;let tmpLeft=Math.min(tmpStartX,tmpCurrentX);let tmpTop=Math.min(tmpStartY,tmpCurrentY);let tmpWidth=Math.abs(tmpCurrentX-tmpStartX);let tmpHeight=Math.abs(tmpCurrentY-tmpStartY);tmpRectEl.style.left=tmpLeft+'px';tmpRectEl.style.top=tmpTop+'px';tmpRectEl.style.width=tmpWidth+'px';tmpRectEl.style.height=tmpHeight+'px';};this._pdfRegionMouseUp=function(pEvent){if(!tmpDragging){return;}tmpDragging=false;let tmpRect=tmpOverlay.getBoundingClientRect();let tmpEndX=pEvent.clientX-tmpRect.left;let tmpEndY=pEvent.clientY-tmpRect.top;let tmpLeft=Math.min(tmpStartX,tmpEndX);let tmpTop=Math.min(tmpStartY,tmpEndY);let tmpWidth=Math.abs(tmpEndX-tmpStartX);let tmpHeight=Math.abs(tmpEndY-tmpStartY);// Ignore tiny drags (likely accidental clicks)
11068
+ if(tmpWidth<5||tmpHeight<5){if(tmpRectEl&&tmpRectEl.parentElement){tmpRectEl.parentElement.removeChild(tmpRectEl);}tmpRectEl=null;return;}// Convert screen pixels to PDF coordinate units
11069
+ let tmpCanvas=document.getElementById('RetoldRemote-PdfCanvas');if(!tmpCanvas){return;}let tmpCanvasWidth=tmpCanvas.width;let tmpCanvasHeight=tmpCanvas.height;let tmpDisplayWidth=tmpCanvas.offsetWidth;let tmpDisplayHeight=tmpCanvas.offsetHeight;// Scale from display pixels to canvas pixels, then to PDF units via scale
11070
+ let tmpScaleX=tmpCanvasWidth/tmpDisplayWidth;let tmpScaleY=tmpCanvasHeight/tmpDisplayHeight;let tmpPdfX=tmpLeft*tmpScaleX/tmpSelf._pdfScale;let tmpPdfY=tmpTop*tmpScaleY/tmpSelf._pdfScale;let tmpPdfWidth=tmpWidth*tmpScaleX/tmpSelf._pdfScale;let tmpPdfHeight=tmpHeight*tmpScaleY/tmpSelf._pdfScale;tmpSelf._pdfPendingRegion={X:Math.round(tmpPdfX*100)/100,Y:Math.round(tmpPdfY*100)/100,Width:Math.round(tmpPdfWidth*100)/100,Height:Math.round(tmpPdfHeight*100)/100};tmpSelf._pdfPendingText=null;// Show label input
11071
+ let tmpLabelBar=document.getElementById('RetoldRemote-PdfLabelInput');if(tmpLabelBar){tmpLabelBar.style.display='';}let tmpField=document.getElementById('RetoldRemote-PdfLabelField');if(tmpField){tmpField.value='';tmpField.placeholder='Label this region\u2026';tmpField.focus();}};tmpOverlay.addEventListener('mousedown',this._pdfRegionMouseDown);tmpOverlay.addEventListener('mousemove',this._pdfRegionMouseMove);tmpOverlay.addEventListener('mouseup',this._pdfRegionMouseUp);},/**
11072
+ * Remove mouse event handlers for region dragging.
11073
+ */_pdfCleanupRegionDrag:function _pdfCleanupRegionDrag(){let tmpOverlay=document.getElementById('RetoldRemote-PdfSelectionOverlay');if(tmpOverlay){if(this._pdfRegionMouseDown){tmpOverlay.removeEventListener('mousedown',this._pdfRegionMouseDown);}if(this._pdfRegionMouseMove){tmpOverlay.removeEventListener('mousemove',this._pdfRegionMouseMove);}if(this._pdfRegionMouseUp){tmpOverlay.removeEventListener('mouseup',this._pdfRegionMouseUp);}}this._pdfRegionMouseDown=null;this._pdfRegionMouseMove=null;this._pdfRegionMouseUp=null;},// -----------------------------------------------------------------
11074
+ // Label save / cancel
11075
+ // -----------------------------------------------------------------
11076
+ /**
11077
+ * Save the labeled selection (text or visual region) to the server.
11078
+ */pdfSaveLabel:function pdfSaveLabel(){let tmpField=document.getElementById('RetoldRemote-PdfLabelField');let tmpLabel=tmpField?tmpField.value.trim():'';let tmpSelf=this;let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpPathParam=tmpProvider?tmpProvider._getPathParam(this._pdfFilePath):encodeURIComponent(this._pdfFilePath);let tmpBody={Path:this._pdfFilePath,Region:{Label:tmpLabel,PageNumber:this._pdfCurrentPage}};if(this._pdfPendingText){tmpBody.Region.Type='text-selection';tmpBody.Region.SelectedText=this._pdfPendingText;}else if(this._pdfPendingRegion){tmpBody.Region.Type='visual-region';tmpBody.Region.X=this._pdfPendingRegion.X;tmpBody.Region.Y=this._pdfPendingRegion.Y;tmpBody.Region.Width=this._pdfPendingRegion.Width;tmpBody.Region.Height=this._pdfPendingRegion.Height;}else{// Nothing pending
11079
+ this.pdfCancelSelection();return;}fetch('/api/media/subimage-regions',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(tmpBody)}).then(function(pResponse){return pResponse.json();}).then(function(pResult){if(pResult&&pResult.Success){tmpSelf._pdfSavedRegions=pResult.Regions||[];tmpSelf._pdfRenderRegionOverlays();let tmpToast=tmpSelf.pict.providers['RetoldRemote-ToastNotification'];if(tmpToast){tmpToast.showToast('Selection saved'+(tmpLabel?': '+tmpLabel:''));}// Update the subimages panel if visible
11080
+ let tmpSubPanel=tmpSelf.pict.views['RetoldRemote-SubimagesPanel'];if(tmpSubPanel){tmpSubPanel.render();}}}).catch(function(pErr){let tmpToast=tmpSelf.pict.providers['RetoldRemote-ToastNotification'];if(tmpToast){tmpToast.showToast('Failed to save selection: '+pErr.message);}});// Clean up
11081
+ this._pdfPendingRegion=null;this._pdfPendingText=null;// Hide the label bar
11082
+ let tmpLabelBar=document.getElementById('RetoldRemote-PdfLabelInput');if(tmpLabelBar){tmpLabelBar.style.display='none';}// Remove the active drag rectangle if any
11083
+ let tmpOverlay=document.getElementById('RetoldRemote-PdfSelectionOverlay');if(tmpOverlay){tmpOverlay.innerHTML='';}},/**
11084
+ * Cancel the current selection without saving.
11085
+ */pdfCancelSelection:function pdfCancelSelection(){this._pdfPendingRegion=null;this._pdfPendingText=null;// Hide the label bar
11086
+ let tmpLabelBar=document.getElementById('RetoldRemote-PdfLabelInput');if(tmpLabelBar){tmpLabelBar.style.display='none';}// Remove any active drag rectangle
11087
+ let tmpOverlay=document.getElementById('RetoldRemote-PdfSelectionOverlay');if(tmpOverlay){tmpOverlay.innerHTML='';}},// -----------------------------------------------------------------
11088
+ // Saved region management
11089
+ // -----------------------------------------------------------------
11090
+ /**
11091
+ * Load saved subimage regions from the server for this PDF.
11092
+ *
11093
+ * @param {string} pFilePath - Relative file path
11094
+ */_pdfLoadSavedRegions:function _pdfLoadSavedRegions(pFilePath){let tmpSelf=this;let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpPathParam=tmpProvider?tmpProvider._getPathParam(pFilePath):encodeURIComponent(pFilePath);fetch('/api/media/subimage-regions?path='+tmpPathParam).then(function(pResponse){return pResponse.json();}).then(function(pResult){if(pResult&&pResult.Success&&Array.isArray(pResult.Regions)){tmpSelf._pdfSavedRegions=pResult.Regions;tmpSelf._pdfRenderRegionOverlays();}}).catch(function(){// Silently ignore — regions are optional
11095
+ });},/**
11096
+ * Render saved region overlays for the current page as colored rectangles
11097
+ * positioned over the canvas.
11098
+ */_pdfRenderRegionOverlays:function _pdfRenderRegionOverlays(){let tmpOverlaysContainer=document.getElementById('RetoldRemote-PdfRegionOverlays');if(!tmpOverlaysContainer){return;}let tmpCanvas=document.getElementById('RetoldRemote-PdfCanvas');if(!tmpCanvas){return;}// Match container dimensions to the canvas
11099
+ tmpOverlaysContainer.style.width=tmpCanvas.offsetWidth+'px';tmpOverlaysContainer.style.height=tmpCanvas.offsetHeight+'px';// Clear existing overlays
11100
+ tmpOverlaysContainer.innerHTML='';let tmpCurrentPage=this._pdfCurrentPage;let tmpScale=this._pdfScale;let tmpDisplayWidth=tmpCanvas.offsetWidth;let tmpCanvasWidth=tmpCanvas.width;let tmpDisplayScale=tmpDisplayWidth/tmpCanvasWidth;for(let i=0;i<this._pdfSavedRegions.length;i++){let tmpRegion=this._pdfSavedRegions[i];// Only show regions for the current page (or regions without a page number)
11101
+ if(tmpRegion.PageNumber&&tmpRegion.PageNumber!==tmpCurrentPage){continue;}// Only render visual-region types as overlays
11102
+ if(tmpRegion.Type!=='visual-region'){continue;}// Convert PDF coordinates to display pixels
11103
+ let tmpLeft=tmpRegion.X*tmpScale*tmpDisplayScale;let tmpTop=tmpRegion.Y*tmpScale*tmpDisplayScale;let tmpWidth=tmpRegion.Width*tmpScale*tmpDisplayScale;let tmpHeight=tmpRegion.Height*tmpScale*tmpDisplayScale;let tmpRectEl=document.createElement('div');tmpRectEl.className='retold-remote-pdf-region-rect';tmpRectEl.style.left=tmpLeft+'px';tmpRectEl.style.top=tmpTop+'px';tmpRectEl.style.width=tmpWidth+'px';tmpRectEl.style.height=tmpHeight+'px';if(tmpRegion.Label){let tmpLabelEl=document.createElement('div');tmpLabelEl.className='retold-remote-pdf-region-label';tmpLabelEl.textContent=tmpRegion.Label;tmpRectEl.appendChild(tmpLabelEl);}tmpOverlaysContainer.appendChild(tmpRectEl);}}};},{}],140:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-AudioExplorer",DefaultRenderable:"RetoldRemote-AudioExplorer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteAudioExplorerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._currentPath='';this._waveformData=null;this._peaks=[];// View state
10907
11104
  this._viewStart=0;// Start of visible range (0..1)
10908
11105
  this._viewEnd=1;// End of visible range (0..1)
10909
11106
  this._minZoom=0.005;// Minimum visible range (0.5% of total)
@@ -11035,7 +11232,7 @@ tmpSelf._applyPendingSelection();tmpSelf._updateSelectionButtons();tmpSelf._draw
11035
11232
  * Show an error message.
11036
11233
  *
11037
11234
  * @param {string} pMessage - Error message
11038
- */_showError(pMessage){let tmpBody=document.getElementById('RetoldRemote-AEX-Body');if(tmpBody){tmpBody.innerHTML='<div class="retold-remote-aex-error">'+'<div class="retold-remote-aex-error-message">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pMessage||'An error occurred.')+'</div>'+'<button class="retold-remote-aex-nav-btn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].goBack()">Back to Audio</button>'+'</div>';}}}RetoldRemoteAudioExplorerView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteAudioExplorerView;},{"pict-view":88}],140:[function(require,module,exports){/**
11235
+ */_showError(pMessage){let tmpBody=document.getElementById('RetoldRemote-AEX-Body');if(tmpBody){tmpBody.innerHTML='<div class="retold-remote-aex-error">'+'<div class="retold-remote-aex-error-message">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pMessage||'An error occurred.')+'</div>'+'<button class="retold-remote-aex-nav-btn" onclick="pict.views[\'RetoldRemote-AudioExplorer\'].goBack()">Back to Audio</button>'+'</div>';}}}RetoldRemoteAudioExplorerView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteAudioExplorerView;},{"pict-view":88}],141:[function(require,module,exports){/**
11039
11236
  * Retold Remote -- Collections Panel View
11040
11237
  *
11041
11238
  * Right-side flyout panel for managing user-defined collections.
@@ -11065,7 +11262,7 @@ let tmpHeader=document.createElement('div');tmpHeader.className='retold-remote-c
11065
11262
  let tmpIsOperationPlan=tmpCollection.CollectionType==='operation-plan';if(tmpIsOperationPlan){// Operation plan controls: summary + execute/undo buttons
11066
11263
  let tmpOpControls=document.createElement('div');tmpOpControls.className='retold-remote-collections-detail-controls retold-remote-collections-op-controls';// Count pending, completed, failed, skipped
11067
11264
  let tmpItems=tmpCollection.Items||[];let tmpPending=0;let tmpCompleted=0;let tmpFailed=0;let tmpSkipped=0;for(let i=0;i<tmpItems.length;i++){let tmpStatus=tmpItems[i].OperationStatus;if(tmpStatus==='completed')tmpCompleted++;else if(tmpStatus==='failed')tmpFailed++;else if(tmpStatus==='skipped')tmpSkipped++;else if(tmpItems[i].Operation)tmpPending++;}let tmpSummary=document.createElement('div');tmpSummary.className='retold-remote-collections-op-summary';let tmpSummaryParts=[];if(tmpPending>0)tmpSummaryParts.push(tmpPending+' pending');if(tmpCompleted>0)tmpSummaryParts.push(tmpCompleted+' done');if(tmpFailed>0)tmpSummaryParts.push(tmpFailed+' failed');if(tmpSkipped>0)tmpSummaryParts.push(tmpSkipped+' skipped');tmpSummary.textContent=tmpSummaryParts.join(' \u00b7 ')||'No operations';tmpOpControls.appendChild(tmpSummary);let tmpBtnRow=document.createElement('div');tmpBtnRow.className='retold-remote-collections-op-buttons';if(tmpPending>0){let tmpExecBtn=document.createElement('button');tmpExecBtn.className='retold-remote-collections-op-execute-btn';tmpExecBtn.textContent='Execute '+tmpPending+' Move'+(tmpPending>1?'s':'');tmpExecBtn.onclick=()=>{tmpExecBtn.disabled=true;tmpExecBtn.textContent='Moving...';tmpManager.executeCollectionOperations(tmpCollection.GUID);};tmpBtnRow.appendChild(tmpExecBtn);}if(tmpCollection.OperationBatchGUID&&tmpCompleted>0){let tmpUndoBtn=document.createElement('button');tmpUndoBtn.className='retold-remote-collections-op-undo-btn';tmpUndoBtn.textContent='Undo';tmpUndoBtn.onclick=()=>{tmpUndoBtn.disabled=true;tmpUndoBtn.textContent='Undoing...';tmpManager.undoCollectionOperations(tmpCollection.GUID);};tmpBtnRow.appendChild(tmpUndoBtn);}tmpOpControls.appendChild(tmpBtnRow);pRoot.appendChild(tmpOpControls);}else{// Standard sort controls for bookmark collections
11068
- let tmpControls=document.createElement('div');tmpControls.className='retold-remote-collections-detail-controls';let tmpSortSelect=document.createElement('select');tmpSortSelect.className='retold-remote-collections-sort-select';let tmpSortOptions=[{value:'manual',label:'Manual'},{value:'name',label:'Name'},{value:'modified',label:'Date Added'},{value:'type',label:'Type'}];for(let i=0;i<tmpSortOptions.length;i++){let tmpOpt=document.createElement('option');tmpOpt.value=tmpSortOptions[i].value;tmpOpt.textContent=tmpSortOptions[i].label;if(tmpCollection.SortMode===tmpSortOptions[i].value){tmpOpt.selected=true;}tmpSortSelect.appendChild(tmpOpt);}tmpSortSelect.onchange=pEvent=>{tmpManager.sortActiveCollection(pEvent.target.value,null);};let tmpDirBtn=document.createElement('button');tmpDirBtn.className='retold-remote-collections-sort-dir';tmpDirBtn.textContent=tmpCollection.SortDirection==='desc'?'\u2193':'\u2191';tmpDirBtn.title=tmpCollection.SortDirection==='desc'?'Descending':'Ascending';tmpDirBtn.onclick=()=>{let tmpNewDir=tmpRemote.ActiveCollection.SortDirection==='desc'?'asc':'desc';tmpManager.sortActiveCollection(null,tmpNewDir);};tmpControls.appendChild(tmpSortSelect);tmpControls.appendChild(tmpDirBtn);pRoot.appendChild(tmpControls);}// Item list
11265
+ let tmpControls=document.createElement('div');tmpControls.className='retold-remote-collections-detail-controls';let tmpSortSelect=document.createElement('select');tmpSortSelect.className='retold-remote-collections-sort-select';let tmpSortOptions=[{value:'manual',label:'Manual'},{value:'name',label:'Name'},{value:'modified',label:'Date Added'},{value:'type',label:'Type'}];for(let i=0;i<tmpSortOptions.length;i++){let tmpOpt=document.createElement('option');tmpOpt.value=tmpSortOptions[i].value;tmpOpt.textContent=tmpSortOptions[i].label;if(tmpCollection.SortMode===tmpSortOptions[i].value){tmpOpt.selected=true;}tmpSortSelect.appendChild(tmpOpt);}tmpSortSelect.onchange=pEvent=>{tmpManager.sortActiveCollection(pEvent.target.value,null);};let tmpDirBtn=document.createElement('button');tmpDirBtn.className='retold-remote-collections-sort-dir';tmpDirBtn.textContent=tmpCollection.SortDirection==='desc'?'\u2193':'\u2191';tmpDirBtn.title=tmpCollection.SortDirection==='desc'?'Descending':'Ascending';tmpDirBtn.onclick=()=>{let tmpNewDir=tmpRemote.ActiveCollection.SortDirection==='desc'?'asc':'desc';tmpManager.sortActiveCollection(null,tmpNewDir);};let tmpExportBtn=document.createElement('button');tmpExportBtn.className='retold-remote-collections-export-btn';tmpExportBtn.textContent='\u21e9 Export';tmpExportBtn.title='Export collection to a folder';tmpExportBtn.onclick=()=>{tmpSelf._showExportDialog(tmpCollection.GUID,tmpCollection.Name);};tmpControls.appendChild(tmpSortSelect);tmpControls.appendChild(tmpDirBtn);tmpControls.appendChild(tmpExportBtn);pRoot.appendChild(tmpControls);}// Item list
11069
11266
  let tmpBody=document.createElement('div');tmpBody.className='retold-remote-collections-body';pRoot.appendChild(tmpBody);if(tmpIsOperationPlan){this._renderOperationItemList(tmpBody,tmpCollection);}else{this._renderItemList(tmpBody,tmpCollection);}}_renderItemList(pBody,pCollection){let tmpSelf=this;let tmpRemote=this.pict.AppData.RetoldRemote;let tmpManager=this.pict.providers['RetoldRemote-CollectionManager'];let tmpItems=pCollection.Items||[];pBody.innerHTML='';if(tmpItems.length===0){let tmpEmpty=document.createElement('div');tmpEmpty.className='retold-remote-collections-empty';tmpEmpty.textContent='No items yet. Browse files and add them to this collection.';pBody.appendChild(tmpEmpty);return;}for(let i=0;i<tmpItems.length;i++){let tmpItem=tmpItems[i];let tmpRow=document.createElement('div');tmpRow.className='retold-remote-collection-item';tmpRow.setAttribute('data-item-id',tmpItem.ID);// Drag handle (for manual sort)
11070
11267
  if(pCollection.SortMode==='manual'){let tmpDrag=document.createElement('div');tmpDrag.className='retold-remote-collection-item-drag';tmpDrag.textContent='\u2630';tmpDrag.draggable=true;tmpDrag.ondragstart=pEvent=>{tmpSelf._draggedItemId=tmpItem.ID;tmpRow.classList.add('dragging');pEvent.dataTransfer.effectAllowed='move';};tmpDrag.ondragend=()=>{tmpRow.classList.remove('dragging');tmpSelf._draggedItemId=null;// Remove all drag-over classes
11071
11268
  let tmpAllItems=pBody.querySelectorAll('.retold-remote-collection-item');for(let j=0;j<tmpAllItems.length;j++){tmpAllItems[j].classList.remove('drag-over');}};tmpRow.appendChild(tmpDrag);}// Drop target events
@@ -11104,7 +11301,22 @@ let tmpDeleteBtn=document.createElement('button');tmpDeleteBtn.className='retold
11104
11301
  */_createEditGroup(pLabel,pType,pValue,pId){let tmpGroup=document.createElement('div');tmpGroup.className='retold-remote-collections-edit-group';let tmpLabel=document.createElement('div');tmpLabel.className='retold-remote-collections-edit-label';tmpLabel.textContent=pLabel;let tmpInput;if(pType==='textarea'){tmpInput=document.createElement('textarea');tmpInput.className='retold-remote-collections-edit-textarea';}else{tmpInput=document.createElement('input');tmpInput.className='retold-remote-collections-edit-input';tmpInput.type='text';}tmpInput.value=pValue;tmpInput.id='retold-remote-'+pId;tmpGroup.appendChild(tmpLabel);tmpGroup.appendChild(tmpInput);return tmpGroup;}// -- Helpers ----------------------------------------------------------
11105
11302
  /**
11106
11303
  * Get a text icon character for an item type.
11107
- */_getTypeIcon(pType,pMediaType){switch(pType){case'folder':case'folder-contents':return'\uD83D\uDCC1';case'subfile':return'\uD83D\uDDC4';case'image-crop':return'\u2702';case'audio-clip':return'\uD83C\uDFB5';case'video-clip':case'video-frame':return'\uD83C\uDFAC';default:break;}switch(pMediaType){case'image':return'\uD83D\uDDBC';case'video':return'\uD83C\uDFAC';case'audio':return'\uD83C\uDFB5';case'document':return'\uD83D\uDCC4';default:return'\uD83D\uDCC4';}}}RetoldRemoteCollectionsPanelView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteCollectionsPanelView;},{"pict-view":88}],141:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-FileInfoPanel",DefaultRenderable:"RetoldRemote-FileInfoPanel",DefaultDestinationAddress:"#RetoldRemote-Info-Container",AutoRender:false,CSS:``,DefaultTemplateRecordAddress:false,Templates:[{Hash:"RetoldRemote-FileInfoPanel",Template:`<div class="retold-remote-info" id="RetoldRemote-Info-Body"></div>`}],Renderables:[{RenderableHash:"RetoldRemote-FileInfoPanel",TemplateHash:"RetoldRemote-FileInfoPanel",DestinationAddress:"#RetoldRemote-Info-Container"}]};class RetoldRemoteFileInfoPanel extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._currentPath=null;this._currentMetadata=null;this._extracting=false;}onAfterRender(){this._refreshForCurrentFile();}/**
11304
+ */_getTypeIcon(pType,pMediaType){switch(pType){case'folder':case'folder-contents':return'\uD83D\uDCC1';case'subfile':return'\uD83D\uDDC4';case'image-crop':return'\u2702';case'audio-clip':return'\uD83C\uDFB5';case'video-clip':case'video-frame':return'\uD83C\uDFAC';default:break;}switch(pMediaType){case'image':return'\uD83D\uDDBC';case'video':return'\uD83C\uDFAC';case'audio':return'\uD83C\uDFB5';case'document':return'\uD83D\uDCC4';default:return'\uD83D\uDCC4';}}// -- Export Dialog -------------------------------------------------------
11305
+ /**
11306
+ * Show the export dialog for a collection.
11307
+ *
11308
+ * @param {string} pGUID - Collection GUID
11309
+ * @param {string} pCollectionName - Collection name (for default folder)
11310
+ */_showExportDialog(pGUID,pCollectionName){let tmpSelf=this;// Build a default folder name from the collection name
11311
+ let tmpDefaultFolder=(pCollectionName||'collection-export').replace(/[<>:"/\\|?*\x00-\x1F]/g,'_').replace(/\s+/g,'_').substring(0,80);// Use the current browsed folder as a prefix if available
11312
+ let tmpRemote=this.pict.AppData.RetoldRemote;let tmpCurrentFolder=tmpRemote.CurrentBrowsedFolder||'';let tmpDefaultPath=tmpCurrentFolder?tmpCurrentFolder.replace(/\/+$/,'')+'/'+tmpDefaultFolder:tmpDefaultFolder;// Create a simple inline prompt in the collections detail area
11313
+ let tmpRoot=document.getElementById('RetoldRemote-Collections-Detail');if(!tmpRoot){return;}// Find or create the export dialog container
11314
+ let tmpExistingDialog=document.getElementById('RetoldRemote-ExportDialog');if(tmpExistingDialog){tmpExistingDialog.parentElement.removeChild(tmpExistingDialog);}let tmpDialog=document.createElement('div');tmpDialog.id='RetoldRemote-ExportDialog';tmpDialog.style.cssText='padding:10px;border-top:1px solid var(--retold-border);background:var(--retold-bg-secondary,#21252b);';tmpDialog.innerHTML='<div style="font-size:0.78rem;color:var(--retold-text);margin-bottom:6px;">Export to folder (within content root):</div>'+'<input type="text" id="RetoldRemote-ExportPath" value="'+tmpDefaultPath.replace(/"/g,'&quot;')+'" '+'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;">'+'<div style="display:flex;gap:6px;">'+'<button id="RetoldRemote-ExportConfirmBtn" style="flex:1;padding:4px 8px;font-size:0.75rem;">Export</button>'+'<button id="RetoldRemote-ExportCancelBtn" style="padding:4px 8px;font-size:0.75rem;">Cancel</button>'+'</div>'+'<div id="RetoldRemote-ExportStatus" style="font-size:0.72rem;color:var(--retold-text-dim);margin-top:6px;display:none;"></div>';tmpRoot.appendChild(tmpDialog);// Focus the path input
11315
+ let tmpPathInput=document.getElementById('RetoldRemote-ExportPath');if(tmpPathInput){tmpPathInput.focus();tmpPathInput.select();}// Cancel handler
11316
+ document.getElementById('RetoldRemote-ExportCancelBtn').onclick=()=>{tmpDialog.parentElement.removeChild(tmpDialog);};// Export handler
11317
+ document.getElementById('RetoldRemote-ExportConfirmBtn').onclick=()=>{let tmpDestPath=tmpPathInput?tmpPathInput.value.trim():'';if(!tmpDestPath){return;}let tmpBtn=document.getElementById('RetoldRemote-ExportConfirmBtn');let tmpStatus=document.getElementById('RetoldRemote-ExportStatus');if(tmpBtn)tmpBtn.disabled=true;if(tmpBtn)tmpBtn.textContent='Exporting\u2026';if(tmpStatus)tmpStatus.style.display='';if(tmpStatus)tmpStatus.textContent='Exporting\u2026';fetch('/api/collections/'+encodeURIComponent(pGUID)+'/export',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({DestinationPath:tmpDestPath})}).then(pResponse=>pResponse.json()).then(pResult=>{if(pResult&&pResult.Success){let tmpMsg='Exported '+pResult.ExportedCount+' of '+pResult.TotalItems+' items';if(pResult.ErrorCount>0){tmpMsg+=' ('+pResult.ErrorCount+' errors)';}tmpMsg+=' to '+pResult.DestinationPath;if(tmpStatus)tmpStatus.textContent=tmpMsg;if(tmpBtn)tmpBtn.textContent='Done';let tmpToast=tmpSelf.pict.providers['RetoldRemote-ToastNotification'];if(tmpToast){tmpToast.showToast(tmpMsg);}// Auto-dismiss after a moment
11318
+ setTimeout(()=>{if(tmpDialog.parentElement){tmpDialog.parentElement.removeChild(tmpDialog);}},3000);}else{let tmpErrMsg=pResult&&pResult.Error||'Export failed';if(tmpStatus)tmpStatus.textContent=tmpErrMsg;if(tmpStatus)tmpStatus.style.color='#e06c75';if(tmpBtn)tmpBtn.textContent='Retry';if(tmpBtn)tmpBtn.disabled=false;}}).catch(pError=>{if(tmpStatus)tmpStatus.textContent='Request failed: '+pError.message;if(tmpStatus)tmpStatus.style.color='#e06c75';if(tmpBtn)tmpBtn.textContent='Retry';if(tmpBtn)tmpBtn.disabled=false;});};// Enter key in input triggers export
11319
+ if(tmpPathInput){tmpPathInput.onkeydown=pEvent=>{if(pEvent.key==='Enter'){document.getElementById('RetoldRemote-ExportConfirmBtn').click();}if(pEvent.key==='Escape'){tmpDialog.parentElement.removeChild(tmpDialog);}};}}}RetoldRemoteCollectionsPanelView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteCollectionsPanelView;},{"pict-view":88}],142:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-FileInfoPanel",DefaultRenderable:"RetoldRemote-FileInfoPanel",DefaultDestinationAddress:"#RetoldRemote-Info-Container",AutoRender:false,CSS:``,DefaultTemplateRecordAddress:false,Templates:[{Hash:"RetoldRemote-FileInfoPanel",Template:`<div class="retold-remote-info" id="RetoldRemote-Info-Body"></div>`}],Renderables:[{RenderableHash:"RetoldRemote-FileInfoPanel",TemplateHash:"RetoldRemote-FileInfoPanel",DestinationAddress:"#RetoldRemote-Info-Container"}]};class RetoldRemoteFileInfoPanel extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._currentPath=null;this._currentMetadata=null;this._extracting=false;}onAfterRender(){this._refreshForCurrentFile();}/**
11108
11320
  * Determine the currently selected file path and fetch metadata.
11109
11321
  */_refreshForCurrentFile(){let tmpRemote=this.pict.AppData.RetoldRemote;let tmpPath='';// Try content editor current file first
11110
11322
  if(this.pict.AppData.ContentEditor&&this.pict.AppData.ContentEditor.CurrentFile){tmpPath=this.pict.AppData.ContentEditor.CurrentFile;}// Fall back to viewer state
@@ -11155,7 +11367,7 @@ tmpHTML+=`<button class="retold-remote-info-extract-btn" onclick="pict.views['Re
11155
11367
  // Formatting utilities
11156
11368
  // ---------------------------------------------------------------
11157
11369
  _row(pLabel,pValue){return`<div class="retold-remote-info-row"><span class="retold-remote-info-label">${this._esc(String(pLabel))}</span><span class="retold-remote-info-value">${this._esc(String(pValue))}</span></div>`;}_esc(pStr){if(!pStr){return'';}return String(pStr).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');}_formatSize(pBytes){if(!pBytes&&pBytes!==0){return'unknown';}let tmpBytes=parseInt(pBytes,10);if(tmpBytes<1024){return tmpBytes+' B';}if(tmpBytes<1024*1024){return(tmpBytes/1024).toFixed(1)+' KB';}if(tmpBytes<1024*1024*1024){return(tmpBytes/(1024*1024)).toFixed(1)+' MB';}return(tmpBytes/(1024*1024*1024)).toFixed(2)+' GB';}_formatDuration(pSeconds){if(!pSeconds&&pSeconds!==0){return'unknown';}let tmpSec=Math.floor(pSeconds);let tmpHours=Math.floor(tmpSec/3600);let tmpMins=Math.floor(tmpSec%3600/60);let tmpRem=tmpSec%60;if(tmpHours>0){return`${tmpHours}:${String(tmpMins).padStart(2,'0')}:${String(tmpRem).padStart(2,'0')}`;}return`${tmpMins}:${String(tmpRem).padStart(2,'0')}`;}_formatBitrate(pBits){if(!pBits){return'unknown';}let tmpBits=parseInt(pBits,10);if(tmpBits<1000){return tmpBits+' bps';}if(tmpBits<1000000){return(tmpBits/1000).toFixed(0)+' kbps';}return(tmpBits/1000000).toFixed(1)+' Mbps';}_formatFrameRate(pRate){if(!pRate){return'unknown';}// ffprobe returns frame rate as "24000/1001" or "30/1"
11158
- if(typeof pRate==='string'&&pRate.includes('/')){let tmpParts=pRate.split('/');let tmpNum=parseFloat(tmpParts[0]);let tmpDen=parseFloat(tmpParts[1]);if(tmpDen>0){return(tmpNum/tmpDen).toFixed(3).replace(/\.?0+$/,'')+' fps';}}return pRate+' fps';}_formatDate(pDateStr){if(!pDateStr){return'unknown';}try{let tmpDate=new Date(pDateStr);if(isNaN(tmpDate.getTime())){return String(pDateStr);}return tmpDate.toLocaleDateString()+' '+tmpDate.toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'});}catch(pError){return String(pDateStr);}}}module.exports=RetoldRemoteFileInfoPanel;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":88}],142:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-Gallery",DefaultRenderable:"RetoldRemote-Gallery-Grid",DefaultDestinationAddress:"#RetoldRemote-Gallery-Container",AutoRender:false,CSS:``,Templates:[],Renderables:[]};class RetoldRemoteGalleryView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._intersectionObserver=null;}// ──────────────────────────────────────────────
11370
+ if(typeof pRate==='string'&&pRate.includes('/')){let tmpParts=pRate.split('/');let tmpNum=parseFloat(tmpParts[0]);let tmpDen=parseFloat(tmpParts[1]);if(tmpDen>0){return(tmpNum/tmpDen).toFixed(3).replace(/\.?0+$/,'')+' fps';}}return pRate+' fps';}_formatDate(pDateStr){if(!pDateStr){return'unknown';}try{let tmpDate=new Date(pDateStr);if(isNaN(tmpDate.getTime())){return String(pDateStr);}return tmpDate.toLocaleDateString()+' '+tmpDate.toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'});}catch(pError){return String(pDateStr);}}}module.exports=RetoldRemoteFileInfoPanel;module.exports.default_configuration=_ViewConfiguration;},{"pict-view":88}],143:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-Gallery",DefaultRenderable:"RetoldRemote-Gallery-Grid",DefaultDestinationAddress:"#RetoldRemote-Gallery-Container",AutoRender:false,CSS:``,Templates:[],Renderables:[]};class RetoldRemoteGalleryView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._intersectionObserver=null;}// ──────────────────────────────────────────────
11159
11371
  // Gallery rendering
11160
11372
  // ──────────────────────────────────────────────
11161
11373
  /**
@@ -11289,11 +11501,16 @@ pEvent.preventDefault();}_hideLongPressTooltip(){if(this._longPressTooltipEl){if
11289
11501
  * Get the media category for a file.
11290
11502
  */_getCategory(pExtension,pType){if(pType==='folder')return'folder';if(pType==='archive')return'archive';// Delegate to the filter/sort provider if available
11291
11503
  let tmpFilterSort=this.pict.providers['RetoldRemote-GalleryFilterSort'];if(tmpFilterSort){return tmpFilterSort.getCategory(pExtension);}// Fallback
11292
- let tmpExt=(pExtension||'').replace(/^\./,'').toLowerCase();if(tmpExt==='png'||tmpExt==='jpg'||tmpExt==='jpeg'||tmpExt==='gif'||tmpExt==='webp')return'image';if(tmpExt==='mp4'||tmpExt==='webm'||tmpExt==='mov')return'video';if(tmpExt==='mp3'||tmpExt==='wav'||tmpExt==='ogg')return'audio';if(tmpExt==='pdf')return'document';return'other';}}RetoldRemoteGalleryView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteGalleryView;},{"pict-view":88}],143:[function(require,module,exports){const libPictView=require('pict-view');const _OSD_CDN_URL='https://cdnjs.cloudflare.com/ajax/libs/openseadragon/4.1.1/openseadragon.min.js';const _ViewConfiguration={ViewIdentifier:"RetoldRemote-ImageExplorer",DefaultRenderable:"RetoldRemote-ImageExplorer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteImageExplorerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._currentPath='';this._osdViewer=null;this._dziData=null;this._osdLoaded=false;this._loading=false;}/**
11504
+ let tmpExt=(pExtension||'').replace(/^\./,'').toLowerCase();if(tmpExt==='png'||tmpExt==='jpg'||tmpExt==='jpeg'||tmpExt==='gif'||tmpExt==='webp')return'image';if(tmpExt==='mp4'||tmpExt==='webm'||tmpExt==='mov')return'video';if(tmpExt==='mp3'||tmpExt==='wav'||tmpExt==='ogg')return'audio';if(tmpExt==='pdf')return'document';return'other';}}RetoldRemoteGalleryView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteGalleryView;},{"pict-view":88}],144:[function(require,module,exports){const libPictView=require('pict-view');const _OSD_CDN_URL='https://cdnjs.cloudflare.com/ajax/libs/openseadragon/4.1.1/openseadragon.min.js';const _ViewConfiguration={ViewIdentifier:"RetoldRemote-ImageExplorer",DefaultRenderable:"RetoldRemote-ImageExplorer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteImageExplorerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._currentPath='';this._osdViewer=null;this._dziData=null;this._osdLoaded=false;this._loading=false;// Selection mode state
11505
+ this._selectionMode=false;this._selectionTracker=null;this._selectionOverlay=null;this._selectionRegion=null;// { X, Y, Width, Height } in image coords
11506
+ this._selectionStart=null;// viewport point where drag began
11507
+ this._savedRegions=[];// loaded from server
11508
+ }/**
11293
11509
  * Show the image explorer for a given image file.
11294
11510
  *
11295
11511
  * @param {string} pFilePath - Relative file path
11296
- */showExplorer(pFilePath){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='image-explorer';tmpRemote.CurrentViewerFile=pFilePath;tmpRemote.CurrentViewerMediaType='image';this._currentPath=pFilePath;this._dziData=null;this._loading=false;// Clean up existing viewer
11512
+ */showExplorer(pFilePath){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='image-explorer';tmpRemote.CurrentViewerFile=pFilePath;tmpRemote.CurrentViewerMediaType='image';this._currentPath=pFilePath;this._dziData=null;this._loading=false;// Reset selection state
11513
+ this._selectionMode=false;this._selectionTracker=null;this._selectionOverlay=null;this._selectionRegion=null;this._selectionStart=null;this._savedRegions=[];// Clean up existing viewer
11297
11514
  if(this._osdViewer){try{this._osdViewer.destroy();}catch(pErr){// ignore
11298
11515
  }this._osdViewer=null;}// Update URL hash.
11299
11516
  // When the current hash is #/view/ for the same file (i.e. the media
@@ -11302,12 +11519,12 @@ if(this._osdViewer){try{this._osdViewer.destroy();}catch(pErr){// ignore
11302
11519
  let tmpFragProvider=this.pict.providers['RetoldRemote-Provider'];let tmpFragId=tmpFragProvider?tmpFragProvider.getFragmentIdentifier(pFilePath):pFilePath;let tmpNewHash='#/explore-image/'+tmpFragId;let tmpCurrentHash=window.location.hash||'';if(tmpCurrentHash.indexOf('#/view/')===0){history.replaceState(null,'',tmpNewHash);}else{window.location.hash=tmpNewHash;}// Show viewer, hide gallery
11303
11520
  let tmpGalleryContainer=document.getElementById('RetoldRemote-Gallery-Container');let tmpViewerContainer=document.getElementById('RetoldRemote-Viewer-Container');if(tmpGalleryContainer)tmpGalleryContainer.style.display='none';if(tmpViewerContainer)tmpViewerContainer.style.display='block';let tmpFileName=pFilePath.replace(/^.*\//,'');let tmpFmt=this.pict.providers['RetoldRemote-FormattingUtilities'];// Build the explorer UI
11304
11521
  let tmpHTML='<div class="retold-remote-iex">';// Header
11305
- tmpHTML+='<div class="retold-remote-iex-header">';tmpHTML+='<button class="retold-remote-iex-nav-btn" onclick="pict.views[\'RetoldRemote-ImageExplorer\'].goBack()" title="Back (Esc)">&larr; Back</button>';tmpHTML+='<div class="retold-remote-iex-title">Image Explorer &mdash; '+tmpFmt.escapeHTML(tmpFileName)+'</div>';tmpHTML+='<div class="retold-remote-iex-actions">';tmpHTML+='<button class="retold-remote-iex-action-btn" onclick="pict.views[\'RetoldRemote-ImageExplorer\'].viewInBrowser()" title="View in standard viewer">&#128444; View</button>';tmpHTML+='</div>';tmpHTML+='</div>';// Info bar
11522
+ tmpHTML+='<div class="retold-remote-iex-header">';tmpHTML+='<button class="retold-remote-iex-nav-btn" onclick="pict.views[\'RetoldRemote-ImageExplorer\'].goBack()" title="Back (Esc)">&larr; Back</button>';tmpHTML+='<div class="retold-remote-iex-title">Image Explorer &mdash; '+tmpFmt.escapeHTML(tmpFileName)+'</div>';tmpHTML+='<div class="retold-remote-iex-actions">';tmpHTML+='<button class="retold-remote-iex-action-btn" id="RetoldRemote-IEX-SelectBtn" onclick="pict.views[\'RetoldRemote-ImageExplorer\'].toggleSelectionMode()" title="Select a region (s)">&#9986; Select</button>';tmpHTML+='<button class="retold-remote-iex-action-btn" onclick="pict.views[\'RetoldRemote-ImageExplorer\'].viewInBrowser()" title="View in standard viewer">&#128444; View</button>';tmpHTML+='</div>';tmpHTML+='</div>';// Info bar
11306
11523
  tmpHTML+='<div class="retold-remote-iex-info" id="RetoldRemote-IEX-Info" style="display:none;"></div>';// Body
11307
11524
  tmpHTML+='<div class="retold-remote-iex-body" id="RetoldRemote-IEX-Body">';tmpHTML+='<div class="retold-remote-iex-loading" id="RetoldRemote-IEX-Loading">';tmpHTML+='<div>Loading image\u2026</div>';tmpHTML+='</div>';tmpHTML+='<div id="RetoldRemote-IEX-Viewer" style="display:none;"></div>';tmpHTML+='</div>';// Controls bar
11308
- tmpHTML+='<div class="retold-remote-iex-controls" id="RetoldRemote-IEX-Controls" style="display:none;">';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-ImageExplorer\'].zoomIn()" title="Zoom In (+)">+ Zoom In</button>';tmpHTML+='<span class="retold-remote-iex-zoom-label" id="RetoldRemote-IEX-ZoomLabel">100%</span>';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-ImageExplorer\'].zoomOut()" title="Zoom Out (-)">- Zoom Out</button>';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-ImageExplorer\'].zoomHome()" title="Fit to view (0)">Fit</button>';tmpHTML+='<span style="flex:1;"></span>';tmpHTML+='<span id="RetoldRemote-IEX-Coords" style="color:var(--retold-text-dim);font-size:0.72rem;"></span>';tmpHTML+='</div>';tmpHTML+='</div>';if(tmpViewerContainer){tmpViewerContainer.innerHTML=tmpHTML;}// Update topbar
11525
+ tmpHTML+='<div class="retold-remote-iex-controls" id="RetoldRemote-IEX-Controls" style="display:none;">';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-ImageExplorer\'].zoomIn()" title="Zoom In (+)">+ Zoom In</button>';tmpHTML+='<span class="retold-remote-iex-zoom-label" id="RetoldRemote-IEX-ZoomLabel">100%</span>';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-ImageExplorer\'].zoomOut()" title="Zoom Out (-)">- Zoom Out</button>';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-ImageExplorer\'].zoomHome()" title="Fit to view (0)">Fit</button>';tmpHTML+='<span style="flex:1;"></span>';tmpHTML+='<span id="RetoldRemote-IEX-LabelInput" style="display:none;">';tmpHTML+='<input type="text" id="RetoldRemote-IEX-LabelField" placeholder="Label this region\u2026" style="background:var(--retold-bg-input,#1e1e1e);color:var(--retold-text,#abb2bf);border:1px solid var(--retold-border,#3e4451);border-radius:4px;padding:2px 8px;font-size:0.78rem;width:180px;margin-right:4px;" onkeydown="if(event.key===\'Enter\'){pict.views[\'RetoldRemote-ImageExplorer\'].saveSelectionLabel();event.preventDefault();event.stopPropagation();}if(event.key===\'Escape\'){pict.views[\'RetoldRemote-ImageExplorer\'].cancelSelection();event.preventDefault();event.stopPropagation();}">';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-ImageExplorer\'].saveSelectionLabel()" style="font-size:0.75rem;padding:2px 8px;">Save</button>';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-ImageExplorer\'].cancelSelection()" style="font-size:0.75rem;padding:2px 8px;margin-left:2px;">Cancel</button>';tmpHTML+='</span>';tmpHTML+='<span id="RetoldRemote-IEX-Coords" style="color:var(--retold-text-dim);font-size:0.72rem;"></span>';tmpHTML+='</div>';tmpHTML+='</div>';if(tmpViewerContainer){tmpViewerContainer.innerHTML=tmpHTML;}// Update topbar
11309
11526
  let tmpTopBar=this.pict.views['ContentEditor-TopBar'];if(tmpTopBar){tmpTopBar.updateInfo();}// Load OpenSeadragon, then decide whether to use simple image or DZI tiles
11310
- this._ensureOSDLoaded(()=>{this._probeAndShow(pFilePath);});}/**
11527
+ let tmpSelfShow=this;this._ensureOSDLoaded(()=>{tmpSelfShow._probeAndShow(pFilePath);tmpSelfShow._loadSavedRegions(pFilePath);});}/**
11311
11528
  * Ensure OpenSeadragon library is loaded (from CDN on first use).
11312
11529
  *
11313
11530
  * @param {Function} fCallback - Called when OSD is ready
@@ -11401,9 +11618,88 @@ let tmpHomeZoom=this._osdViewer.viewport.getHomeZoom();let tmpPercent=Math.round
11401
11618
  * Zoom out by a step.
11402
11619
  */zoomOut(){if(this._osdViewer){let tmpCurrentZoom=this._osdViewer.viewport.getZoom();this._osdViewer.viewport.zoomTo(tmpCurrentZoom/1.5);}}/**
11403
11620
  * Reset to home (fit to view).
11404
- */zoomHome(){if(this._osdViewer){this._osdViewer.viewport.goHome();}}/**
11621
+ */zoomHome(){if(this._osdViewer){this._osdViewer.viewport.goHome();}}// -----------------------------------------------------------------
11622
+ // Selection mode — draw rectangles to create labeled subimage regions
11623
+ // -----------------------------------------------------------------
11624
+ /**
11625
+ * Toggle selection mode on/off.
11626
+ */toggleSelectionMode(){if(this._selectionMode){this._exitSelectionMode();}else{this._enterSelectionMode();}}/**
11627
+ * Enter selection mode: disable panning, install drag tracker.
11628
+ */_enterSelectionMode(){if(!this._osdViewer){return;}this._selectionMode=true;// Highlight the Select button
11629
+ let tmpBtn=document.getElementById('RetoldRemote-IEX-SelectBtn');if(tmpBtn){tmpBtn.style.background='rgba(97, 175, 239, 0.4)';tmpBtn.style.color='#fff';}// Disable OSD panning so drag draws a selection instead
11630
+ this._osdViewer.setMouseNavEnabled(false);let tmpSelf=this;let tmpViewerDiv=document.getElementById('RetoldRemote-IEX-Viewer');if(!tmpViewerDiv){return;}tmpViewerDiv.style.cursor='crosshair';this._selectionTracker=new OpenSeadragon.MouseTracker({element:tmpViewerDiv,pressHandler:function(pEvent){tmpSelf._onSelectionPress(pEvent);},dragHandler:function(pEvent){tmpSelf._onSelectionDrag(pEvent);},releaseHandler:function(pEvent){tmpSelf._onSelectionRelease(pEvent);}});}/**
11631
+ * Exit selection mode: re-enable panning, remove drag tracker.
11632
+ */_exitSelectionMode(){this._selectionMode=false;let tmpBtn=document.getElementById('RetoldRemote-IEX-SelectBtn');if(tmpBtn){tmpBtn.style.background='';tmpBtn.style.color='';}if(this._osdViewer){this._osdViewer.setMouseNavEnabled(true);}if(this._selectionTracker){this._selectionTracker.destroy();this._selectionTracker=null;}let tmpViewerDiv=document.getElementById('RetoldRemote-IEX-Viewer');if(tmpViewerDiv){tmpViewerDiv.style.cursor='';}// Remove in-progress selection overlay (keep saved ones)
11633
+ this._removeActiveSelectionOverlay();this._selectionRegion=null;this._selectionStart=null;// Hide label input
11634
+ let tmpLabelWrap=document.getElementById('RetoldRemote-IEX-LabelInput');if(tmpLabelWrap){tmpLabelWrap.style.display='none';}let tmpCoords=document.getElementById('RetoldRemote-IEX-Coords');if(tmpCoords){tmpCoords.style.display='';}}/**
11635
+ * Handle the start of a selection drag.
11636
+ */_onSelectionPress(pEvent){if(!this._osdViewer){return;}// Remove any previous in-progress selection overlay
11637
+ this._removeActiveSelectionOverlay();this._selectionStart=this._osdViewer.viewport.pointFromPixel(pEvent.position);// Create the selection rectangle overlay element
11638
+ let tmpOverlay=document.createElement('div');tmpOverlay.id='RetoldRemote-IEX-ActiveSelection';tmpOverlay.style.cssText='border: 2px solid rgba(97, 175, 239, 0.9); background: rgba(97, 175, 239, 0.15); pointer-events: none;';this._selectionOverlay=tmpOverlay;// Add the overlay at zero size, will expand during drag
11639
+ this._osdViewer.addOverlay({element:tmpOverlay,location:new OpenSeadragon.Rect(this._selectionStart.x,this._selectionStart.y,0,0)});}/**
11640
+ * Handle selection dragging — update the rectangle size.
11641
+ */_onSelectionDrag(pEvent){if(!this._osdViewer||!this._selectionStart||!this._selectionOverlay){return;}let tmpCurrent=this._osdViewer.viewport.pointFromPixel(pEvent.position);let tmpX=Math.min(this._selectionStart.x,tmpCurrent.x);let tmpY=Math.min(this._selectionStart.y,tmpCurrent.y);let tmpW=Math.abs(tmpCurrent.x-this._selectionStart.x);let tmpH=Math.abs(tmpCurrent.y-this._selectionStart.y);this._osdViewer.updateOverlay(this._selectionOverlay,new OpenSeadragon.Rect(tmpX,tmpY,tmpW,tmpH));}/**
11642
+ * Handle selection release — compute image-coordinate region and show label input.
11643
+ */_onSelectionRelease(pEvent){if(!this._osdViewer||!this._selectionStart){return;}let tmpEnd=this._osdViewer.viewport.pointFromPixel(pEvent.position);// Convert viewport rectangle to image pixel coordinates
11644
+ let tmpVpX=Math.min(this._selectionStart.x,tmpEnd.x);let tmpVpY=Math.min(this._selectionStart.y,tmpEnd.y);let tmpVpW=Math.abs(tmpEnd.x-this._selectionStart.x);let tmpVpH=Math.abs(tmpEnd.y-this._selectionStart.y);let tmpTopLeft=this._osdViewer.viewport.viewportToImageCoordinates(new OpenSeadragon.Point(tmpVpX,tmpVpY));let tmpBottomRight=this._osdViewer.viewport.viewportToImageCoordinates(new OpenSeadragon.Point(tmpVpX+tmpVpW,tmpVpY+tmpVpH));let tmpRegion={X:Math.max(0,Math.round(tmpTopLeft.x)),Y:Math.max(0,Math.round(tmpTopLeft.y)),Width:Math.round(tmpBottomRight.x-tmpTopLeft.x),Height:Math.round(tmpBottomRight.y-tmpTopLeft.y)};// Clamp to image dimensions
11645
+ if(this._dziData){if(tmpRegion.X+tmpRegion.Width>this._dziData.Width){tmpRegion.Width=this._dziData.Width-tmpRegion.X;}if(tmpRegion.Y+tmpRegion.Height>this._dziData.Height){tmpRegion.Height=this._dziData.Height-tmpRegion.Y;}}// Ignore tiny selections (likely accidental clicks)
11646
+ if(tmpRegion.Width<5||tmpRegion.Height<5){this._removeActiveSelectionOverlay();return;}this._selectionRegion=tmpRegion;// Show the inline label input in the controls bar
11647
+ let tmpLabelWrap=document.getElementById('RetoldRemote-IEX-LabelInput');let tmpCoords=document.getElementById('RetoldRemote-IEX-Coords');if(tmpLabelWrap){tmpLabelWrap.style.display='';}if(tmpCoords){tmpCoords.style.display='none';}// Focus the label field
11648
+ let tmpField=document.getElementById('RetoldRemote-IEX-LabelField');if(tmpField){tmpField.value='';tmpField.focus();}}/**
11649
+ * Save the current selection with the entered label.
11650
+ */saveSelectionLabel(){if(!this._selectionRegion){return;}let tmpField=document.getElementById('RetoldRemote-IEX-LabelField');let tmpLabel=tmpField?tmpField.value.trim():'';let tmpSelf=this;let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpPathParam=tmpProvider?tmpProvider._getPathParam(this._currentPath):encodeURIComponent(this._currentPath);fetch('/api/media/subimage-regions',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({Path:this._currentPath,Region:{Label:tmpLabel,X:this._selectionRegion.X,Y:this._selectionRegion.Y,Width:this._selectionRegion.Width,Height:this._selectionRegion.Height}})}).then(pResponse=>pResponse.json()).then(pResult=>{if(pResult&&pResult.Success){tmpSelf._savedRegions=pResult.Regions||[];// Remove the active selection overlay and render persistent ones
11651
+ tmpSelf._removeActiveSelectionOverlay();tmpSelf._renderSavedRegionOverlays();// Notify the user
11652
+ let tmpToast=tmpSelf.pict.providers['RetoldRemote-ToastNotification'];if(tmpToast){tmpToast.showToast('Subimage region saved'+(tmpLabel?': '+tmpLabel:''));}// Update the sidebar panel if visible
11653
+ let tmpSubPanel=tmpSelf.pict.views['RetoldRemote-SubimagesPanel'];if(tmpSubPanel){tmpSubPanel.render();}}}).catch(pErr=>{let tmpToast=tmpSelf.pict.providers['RetoldRemote-ToastNotification'];if(tmpToast){tmpToast.showToast('Failed to save region: '+pErr.message);}});// Hide label input, show coords
11654
+ this._selectionRegion=null;this._selectionStart=null;let tmpLabelWrap=document.getElementById('RetoldRemote-IEX-LabelInput');if(tmpLabelWrap){tmpLabelWrap.style.display='none';}let tmpCoords=document.getElementById('RetoldRemote-IEX-Coords');if(tmpCoords){tmpCoords.style.display='';}}/**
11655
+ * Cancel the current in-progress selection.
11656
+ */cancelSelection(){this._removeActiveSelectionOverlay();this._selectionRegion=null;this._selectionStart=null;let tmpLabelWrap=document.getElementById('RetoldRemote-IEX-LabelInput');if(tmpLabelWrap){tmpLabelWrap.style.display='none';}let tmpCoords=document.getElementById('RetoldRemote-IEX-Coords');if(tmpCoords){tmpCoords.style.display='';}}/**
11657
+ * Remove the active (in-progress) selection overlay.
11658
+ */_removeActiveSelectionOverlay(){let tmpActive=document.getElementById('RetoldRemote-IEX-ActiveSelection');if(tmpActive&&this._osdViewer){try{this._osdViewer.removeOverlay(tmpActive);}catch(pErr){// May not be an overlay; just remove from DOM
11659
+ if(tmpActive.parentElement){tmpActive.parentElement.removeChild(tmpActive);}}}this._selectionOverlay=null;}// -----------------------------------------------------------------
11660
+ // Saved region overlays
11661
+ // -----------------------------------------------------------------
11662
+ /**
11663
+ * Load saved subimage regions from the server.
11664
+ *
11665
+ * @param {string} pFilePath - Relative file path
11666
+ */_loadSavedRegions(pFilePath){let tmpSelf=this;let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpPathParam=tmpProvider?tmpProvider._getPathParam(pFilePath):encodeURIComponent(pFilePath);fetch('/api/media/subimage-regions?path='+tmpPathParam).then(pResponse=>pResponse.json()).then(pResult=>{if(pResult&&pResult.Success&&Array.isArray(pResult.Regions)){tmpSelf._savedRegions=pResult.Regions;tmpSelf._renderSavedRegionOverlays();}}).catch(()=>{// Silently ignore — regions are optional
11667
+ });}/**
11668
+ * Render all saved regions as OSD overlays with colored borders and labels.
11669
+ */_renderSavedRegionOverlays(){if(!this._osdViewer){return;}// Remove existing saved-region overlays
11670
+ let tmpExisting=document.querySelectorAll('.retold-remote-iex-region-overlay');for(let i=0;i<tmpExisting.length;i++){try{this._osdViewer.removeOverlay(tmpExisting[i]);}catch(pErr){if(tmpExisting[i].parentElement){tmpExisting[i].parentElement.removeChild(tmpExisting[i]);}}}// Render each saved region
11671
+ for(let i=0;i<this._savedRegions.length;i++){let tmpRegion=this._savedRegions[i];this._addRegionOverlay(tmpRegion);}}/**
11672
+ * Add a single region overlay to the OSD viewer.
11673
+ *
11674
+ * @param {object} pRegion - { ID, Label, X, Y, Width, Height }
11675
+ */_addRegionOverlay(pRegion){if(!this._osdViewer||!this._dziData){return;}let tmpEl=document.createElement('div');tmpEl.className='retold-remote-iex-region-overlay';tmpEl.setAttribute('data-region-id',pRegion.ID);tmpEl.style.cssText='border: 2px solid rgba(229, 192, 123, 0.85); background: rgba(229, 192, 123, 0.08); pointer-events: none; position: relative;';// Label badge
11676
+ if(pRegion.Label){let tmpLabelEl=document.createElement('span');tmpLabelEl.style.cssText='position:absolute;top:-1px;left:-1px;background:rgba(229,192,123,0.9);color:#282c34;font-size:0.65rem;padding:1px 5px;border-radius:0 0 3px 0;white-space:nowrap;pointer-events:none;';tmpLabelEl.textContent=pRegion.Label;tmpEl.appendChild(tmpLabelEl);}// Convert image coordinates to viewport coordinates
11677
+ let tmpImageRect=new OpenSeadragon.Rect(pRegion.X,pRegion.Y,pRegion.Width,pRegion.Height);let tmpViewportRect=this._osdViewer.viewport.imageToViewportRectangle(tmpImageRect);this._osdViewer.addOverlay({element:tmpEl,location:tmpViewportRect});}/**
11678
+ * Navigate to (zoom into) a specific saved region by ID.
11679
+ *
11680
+ * @param {string} pRegionID - The region ID to navigate to
11681
+ */zoomToRegion(pRegionID){if(!this._osdViewer||!this._dziData){return;}let tmpRegion=null;for(let i=0;i<this._savedRegions.length;i++){if(this._savedRegions[i].ID===pRegionID){tmpRegion=this._savedRegions[i];break;}}if(!tmpRegion){return;}// Convert image rect to viewport rect and fit to it
11682
+ let tmpImageRect=new OpenSeadragon.Rect(tmpRegion.X,tmpRegion.Y,tmpRegion.Width,tmpRegion.Height);let tmpViewportRect=this._osdViewer.viewport.imageToViewportRectangle(tmpImageRect);this._osdViewer.viewport.fitBounds(tmpViewportRect);}/**
11683
+ * Delete a saved region by ID.
11684
+ *
11685
+ * @param {string} pRegionID - The region ID to delete
11686
+ */deleteRegion(pRegionID){let tmpSelf=this;let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpPathParam=tmpProvider?tmpProvider._getPathParam(this._currentPath):encodeURIComponent(this._currentPath);fetch('/api/media/subimage-regions/'+encodeURIComponent(pRegionID)+'?path='+tmpPathParam,{method:'DELETE'}).then(pResponse=>pResponse.json()).then(pResult=>{if(pResult&&pResult.Success){tmpSelf._savedRegions=pResult.Regions||[];tmpSelf._renderSavedRegionOverlays();let tmpToast=tmpSelf.pict.providers['RetoldRemote-ToastNotification'];if(tmpToast){tmpToast.showToast('Region deleted');}// Update sidebar
11687
+ let tmpSubPanel=tmpSelf.pict.views['RetoldRemote-SubimagesPanel'];if(tmpSubPanel){tmpSubPanel.render();}}}).catch(()=>{// ignore
11688
+ });}/**
11689
+ * Get the current selection region (for use by collection add).
11690
+ *
11691
+ * @returns {object|null} The current selection or null
11692
+ */getActiveSelection(){return this._selectionRegion;}/**
11693
+ * Get the saved regions array.
11694
+ *
11695
+ * @returns {Array}
11696
+ */getSavedRegions(){return this._savedRegions;}// -----------------------------------------------------------------
11697
+ // Navigation
11698
+ // -----------------------------------------------------------------
11699
+ /**
11405
11700
  * Navigate back to the gallery / file listing.
11406
- */goBack(){// Destroy the OSD viewer
11701
+ */goBack(){// Clean up selection mode
11702
+ if(this._selectionMode){this._exitSelectionMode();}// Destroy the OSD viewer
11407
11703
  if(this._osdViewer){try{this._osdViewer.destroy();}catch(pErr){// ignore
11408
11704
  }this._osdViewer=null;}let tmpNav=this.pict.providers['RetoldRemote-GalleryNavigation'];if(tmpNav){tmpNav.closeViewer();}}/**
11409
11705
  * Leave the image explorer and view the image in the standard viewer.
@@ -11413,7 +11709,7 @@ if(this._osdViewer){try{this._osdViewer.destroy();}catch(pErr){// ignore
11413
11709
  * Show an error message.
11414
11710
  *
11415
11711
  * @param {string} pMessage - Error message
11416
- */_showError(pMessage){let tmpLoading=document.getElementById('RetoldRemote-IEX-Loading');if(tmpLoading){tmpLoading.style.display='none';}let tmpBody=document.getElementById('RetoldRemote-IEX-Body');if(tmpBody){let tmpFmt=this.pict.providers['RetoldRemote-FormattingUtilities'];tmpBody.innerHTML='<div class="retold-remote-iex-error">'+'<div class="retold-remote-iex-error-message">'+tmpFmt.escapeHTML(pMessage||'An error occurred.')+'</div>'+'<button class="retold-remote-iex-nav-btn" onclick="pict.views[\'RetoldRemote-ImageExplorer\'].goBack()">Back to Image</button>'+'</div>';}}}RetoldRemoteImageExplorerView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteImageExplorerView;},{"pict-view":88}],144:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-ImageViewer",DefaultRenderable:"RetoldRemote-ImageViewer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteImageViewerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._zoomLevel=1;this._naturalWidth=0;this._naturalHeight=0;this._resizeHandler=null;}/**
11712
+ */_showError(pMessage){let tmpLoading=document.getElementById('RetoldRemote-IEX-Loading');if(tmpLoading){tmpLoading.style.display='none';}let tmpBody=document.getElementById('RetoldRemote-IEX-Body');if(tmpBody){let tmpFmt=this.pict.providers['RetoldRemote-FormattingUtilities'];tmpBody.innerHTML='<div class="retold-remote-iex-error">'+'<div class="retold-remote-iex-error-message">'+tmpFmt.escapeHTML(pMessage||'An error occurred.')+'</div>'+'<button class="retold-remote-iex-nav-btn" onclick="pict.views[\'RetoldRemote-ImageExplorer\'].goBack()">Back to Image</button>'+'</div>';}}}RetoldRemoteImageExplorerView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteImageExplorerView;},{"pict-view":88}],145:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-ImageViewer",DefaultRenderable:"RetoldRemote-ImageViewer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteImageViewerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._zoomLevel=1;this._naturalWidth=0;this._naturalHeight=0;this._resizeHandler=null;}/**
11417
11713
  * Called when the image finishes loading. Captures the natural
11418
11714
  * dimensions and applies the current fit mode.
11419
11715
  *
@@ -11424,8 +11720,8 @@ if(this._osdViewer){try{this._osdViewer.destroy();}catch(pErr){// ignore
11424
11720
  */initImage(){let tmpImg=document.getElementById('RetoldRemote-ImageViewer-Img');if(!tmpImg){return;}this._naturalWidth=tmpImg.naturalWidth;this._naturalHeight=tmpImg.naturalHeight;this._zoomLevel=1;this._applyDisplay();// Recalculate on window resize
11425
11721
  if(this._resizeHandler){window.removeEventListener('resize',this._resizeHandler);}let tmpSelf=this;let tmpResizeTimer=null;this._resizeHandler=function(){clearTimeout(tmpResizeTimer);tmpResizeTimer=setTimeout(function(){tmpSelf._applyDisplay();},100);};window.addEventListener('resize',this._resizeHandler);// Always show the Explore button so `e` works on any image
11426
11722
  this._showExploreButton();}/**
11427
- * Show the explore button for opening the deep-zoom explorer.
11428
- */_showExploreButton(){let tmpBtn=document.getElementById('RetoldRemote-ImageExploreBtn');if(tmpBtn){tmpBtn.style.display='';}}/**
11723
+ * Show the explore button in the header nav bar.
11724
+ */_showExploreButton(){let tmpBtn=document.getElementById('RetoldRemote-HeaderExploreBtn');if(tmpBtn){tmpBtn.style.display='';}}/**
11429
11725
  * Get the current fit mode from AppData.
11430
11726
  *
11431
11727
  * @returns {string} 'fit' | 'auto' | 'original'
@@ -11459,7 +11755,7 @@ return{width:tmpNW,height:tmpNH};}}}/**
11459
11755
  * @param {string} pMode - The mode identifier
11460
11756
  */_showFitModeIndicator(pMode){let tmpLabels={'fit':'Fit to Window','auto':'Original if Smaller','original':'Original Size'};let tmpLabel=tmpLabels[pMode]||pMode;this.pict.providers['RetoldRemote-ToastNotification'].showOverlayIndicator(tmpLabel,1200);}/**
11461
11757
  * Clean up resize handler when navigating away.
11462
- */cleanup(){if(this._resizeHandler){window.removeEventListener('resize',this._resizeHandler);this._resizeHandler=null;}}}RetoldRemoteImageViewerView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteImageViewerView;},{"pict-view":88}],145:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"ContentEditor-Layout",DefaultRenderable:"RetoldRemote-Layout-Shell",DefaultDestinationAddress:"#ContentEditor-Application-Container",AutoRender:false,CSS:``,Templates:[{Hash:"RetoldRemote-Layout-Shell",Template:/*html*/`
11758
+ */cleanup(){if(this._resizeHandler){window.removeEventListener('resize',this._resizeHandler);this._resizeHandler=null;}}}RetoldRemoteImageViewerView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteImageViewerView;},{"pict-view":88}],146:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"ContentEditor-Layout",DefaultRenderable:"RetoldRemote-Layout-Shell",DefaultDestinationAddress:"#ContentEditor-Application-Container",AutoRender:false,CSS:``,Templates:[{Hash:"RetoldRemote-Layout-Shell",Template:/*html*/`
11463
11759
  <div id="ContentEditor-TopBar-Container"></div>
11464
11760
  <div class="content-editor-body">
11465
11761
  <div class="content-editor-sidebar-wrap" style="width: 250px;">
@@ -11468,6 +11764,7 @@ return{width:tmpNW,height:tmpNH};}}}/**
11468
11764
  <button class="content-editor-sidebar-tab active" data-tab="files" onclick="pict.views['ContentEditor-Layout'].switchSidebarTab('files')">Files</button>
11469
11765
  <button class="content-editor-sidebar-tab" data-tab="favorites" onclick="pict.views['ContentEditor-Layout'].switchSidebarTab('favorites')">Favorites</button>
11470
11766
  <button class="content-editor-sidebar-tab" data-tab="info" onclick="pict.views['ContentEditor-Layout'].switchSidebarTab('info')">Info</button>
11767
+ <button class="content-editor-sidebar-tab" data-tab="subimages" onclick="pict.views['ContentEditor-Layout'].switchSidebarTab('subimages')">Regions</button>
11471
11768
  <button class="content-editor-sidebar-tab" data-tab="settings" onclick="pict.views['ContentEditor-Layout'].switchSidebarTab('settings')">Settings</button>
11472
11769
  <button class="content-editor-sidebar-tab content-editor-sidebar-tab-collections" data-tab="collections" onclick="pict.views['ContentEditor-Layout'].switchSidebarTab('collections')" style="display:none;">Collections</button>
11473
11770
  </div>
@@ -11476,6 +11773,7 @@ return{width:tmpNW,height:tmpNH};}}}/**
11476
11773
  <div id="RetoldRemote-Favorites-Body"></div>
11477
11774
  </div>
11478
11775
  <div class="content-editor-sidebar-pane" data-pane="info" id="RetoldRemote-Info-Container" style="display:none"></div>
11776
+ <div class="content-editor-sidebar-pane" data-pane="subimages" id="RetoldRemote-Subimages-Container" style="display:none"></div>
11479
11777
  <div class="content-editor-sidebar-pane" data-pane="settings" id="RetoldRemote-Settings-Container" style="display:none"></div>
11480
11778
  <div class="content-editor-sidebar-pane" data-pane="collections" id="RetoldRemote-Collections-MobilePane" style="display:none"></div>
11481
11779
  </div>
@@ -11504,7 +11802,8 @@ let tmpSelf=this;window.addEventListener('hashchange',()=>{tmpSelf.pict.PictAppl
11504
11802
  if(!tmpIsCollapsed){if(this.isMobileDrawer()){let tmpHeight=tmpRemote.SidebarDrawerHeight||Math.round(window.innerHeight*0.33);tmpWrap.style.height=tmpHeight+'px';}else if(tmpRemote.SidebarWidth){tmpWrap.style.width=tmpRemote.SidebarWidth+'px';}}this.pict.PictApplication.saveSettings();// Recalculate gallery columns after sidebar resize
11505
11803
  let tmpNavProvider=this.pict.providers['RetoldRemote-GalleryNavigation'];if(tmpNavProvider){setTimeout(()=>tmpNavProvider.recalculateColumns(),250);}}switchSidebarTab(pTab){// Update tab buttons
11506
11804
  let tmpTabs=document.querySelectorAll('.content-editor-sidebar-tab');tmpTabs.forEach(pEl=>{pEl.classList.toggle('active',pEl.getAttribute('data-tab')===pTab);});// Update panes
11507
- let tmpPanes=document.querySelectorAll('.content-editor-sidebar-pane');tmpPanes.forEach(pEl=>{pEl.style.display=pEl.getAttribute('data-pane')===pTab?'':'none';});// Render settings panel on demand
11805
+ let tmpPanes=document.querySelectorAll('.content-editor-sidebar-pane');tmpPanes.forEach(pEl=>{pEl.style.display=pEl.getAttribute('data-pane')===pTab?'':'none';});// Subimages tab: render the subimages panel
11806
+ if(pTab==='subimages'){let tmpSubimagesView=this.pict.views['RetoldRemote-SubimagesPanel'];if(tmpSubimagesView){tmpSubimagesView.render();}}// Render settings panel on demand
11508
11807
  if(pTab==='settings'){let tmpSettingsView=this.pict.views['RetoldRemote-SettingsPanel'];if(tmpSettingsView){tmpSettingsView.render();}}// Favorites tab: render the favorites list
11509
11808
  if(pTab==='favorites'){this.renderFavoritesList();}// Info tab: render the file info panel
11510
11809
  if(pTab==='info'){let tmpInfoView=this.pict.views['RetoldRemote-FileInfoPanel'];if(tmpInfoView){tmpInfoView.render();}}// Collections tab: move the collections container into the mobile pane
@@ -11527,7 +11826,7 @@ let tmpEscapedPath=tmpPath.replace(/'/g,"\\'");tmpHTML+='<div class="retold-remo
11527
11826
  */_setupCollectionsResizeHandle(){let tmpHandle=document.querySelector('.retold-remote-collections-resize-handle');let tmpWrap=document.getElementById('RetoldRemote-Collections-Wrap');if(!tmpHandle||!tmpWrap){return;}let tmpSelf=this;let tmpStartX=0;let tmpStartWidth=0;function onDragStart(pEvent){if(tmpWrap.classList.contains('collapsed')){return;}pEvent.preventDefault();tmpSelf._collectionsDragging=true;tmpHandle.classList.add('dragging');let tmpClientX=pEvent.touches?pEvent.touches[0].clientX:pEvent.clientX;tmpStartX=tmpClientX;tmpStartWidth=tmpWrap.getBoundingClientRect().width;document.addEventListener('mousemove',onDragMove);document.addEventListener('mouseup',onDragEnd);document.addEventListener('touchmove',onDragMove,{passive:false});document.addEventListener('touchend',onDragEnd);}function onDragMove(pEvent){if(!tmpSelf._collectionsDragging){return;}pEvent.preventDefault();let tmpClientX=pEvent.touches?pEvent.touches[0].clientX:pEvent.clientX;// Dragging left (negative deltaX) increases width
11528
11827
  let tmpDelta=tmpStartX-tmpClientX;let tmpNewWidth=Math.max(150,Math.min(600,tmpStartWidth+tmpDelta));tmpWrap.style.width=tmpNewWidth+'px';}function onDragEnd(){if(!tmpSelf._collectionsDragging){return;}tmpSelf._collectionsDragging=false;tmpHandle.classList.remove('dragging');let tmpRemote=tmpSelf.pict.AppData.RetoldRemote;tmpRemote.CollectionsPanelWidth=tmpWrap.getBoundingClientRect().width;tmpSelf.pict.PictApplication.saveSettings();document.removeEventListener('mousemove',onDragMove);document.removeEventListener('mouseup',onDragEnd);document.removeEventListener('touchmove',onDragMove);document.removeEventListener('touchend',onDragEnd);// Recalculate gallery columns
11529
11828
  let tmpGalleryNav=tmpSelf.pict.providers['RetoldRemote-GalleryNavigation'];if(tmpGalleryNav&&typeof tmpGalleryNav.recalculateColumns==='function'){tmpGalleryNav.recalculateColumns();}}tmpHandle.addEventListener('mousedown',onDragStart);tmpHandle.addEventListener('touchstart',onDragStart,{passive:false});// Double-click collapses the collections panel
11530
- tmpHandle.addEventListener('dblclick',function(pEvent){pEvent.preventDefault();tmpSelf.toggleCollectionsPanel();});}}RetoldRemoteLayoutView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteLayoutView;},{"pict-view":88}],146:[function(require,module,exports){const libPictView=require('pict-view');const _MediaViewerEbookViewer=require('./MediaViewer-EbookViewer');const _MediaViewerCodeViewer=require('./MediaViewer-CodeViewer');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-MediaViewer",DefaultRenderable:"RetoldRemote-MediaViewer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteMediaViewerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._swipeStartX=0;this._swipeStartY=0;this._swipeTouchCount=0;this._swipeHandlers=null;this._dfExitHandlers=null;}/**
11829
+ tmpHandle.addEventListener('dblclick',function(pEvent){pEvent.preventDefault();tmpSelf.toggleCollectionsPanel();});}}RetoldRemoteLayoutView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteLayoutView;},{"pict-view":88}],147:[function(require,module,exports){const libPictView=require('pict-view');const _MediaViewerEbookViewer=require('./MediaViewer-EbookViewer');const _MediaViewerCodeViewer=require('./MediaViewer-CodeViewer');const _MediaViewerPdfViewer=require('./MediaViewer-PdfViewer');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-MediaViewer",DefaultRenderable:"RetoldRemote-MediaViewer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteMediaViewerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._swipeStartX=0;this._swipeStartY=0;this._swipeTouchCount=0;this._swipeHandlers=null;this._dfExitHandlers=null;}/**
11531
11830
  * Show the media viewer for a given file.
11532
11831
  *
11533
11832
  * @param {string} pFilePath - Relative file path
@@ -11535,7 +11834,7 @@ tmpHandle.addEventListener('dblclick',function(pEvent){pEvent.preventDefault();t
11535
11834
  */showMedia(pFilePath,pMediaType){let tmpRemote=this.pict.AppData.RetoldRemote;tmpRemote.ActiveMode='viewer';tmpRemote.CurrentViewerFile=pFilePath;tmpRemote.CurrentViewerMediaType=pMediaType;tmpRemote.VideoMenuActive=pMediaType==='video';// Show viewer, hide gallery
11536
11835
  let tmpGalleryContainer=document.getElementById('RetoldRemote-Gallery-Container');let tmpViewerContainer=document.getElementById('RetoldRemote-Viewer-Container');if(tmpGalleryContainer)tmpGalleryContainer.style.display='none';if(tmpViewerContainer)tmpViewerContainer.style.display='block';let tmpFileName=pFilePath.replace(/^.*\//,'');let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpContentURL=tmpProvider?tmpProvider.getContentURL(pFilePath):'/content/'+encodeURIComponent(pFilePath);// Build the viewer HTML
11537
11836
  let tmpHTML='<div class="retold-remote-viewer">';// Header with nav
11538
- tmpHTML+='<div class="retold-remote-viewer-header">';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].closeViewer()" title="Back (Esc)">&larr; Back</button>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].prevFile()" title="Previous (k)">&lsaquo; Prev</button>';tmpHTML+='<div class="retold-remote-viewer-title">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpFileName)+'</div>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].nextFile()" title="Next (j)">Next &rsaquo;</button>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._toggleFileInfo()" title="Info (i)">&#9432;</button>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].toggleDistractionFree()" title="Distraction-Free (d)">&#9634;</button>';tmpHTML+='</div>';// Body with media content
11837
+ tmpHTML+='<div class="retold-remote-viewer-header">';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].closeViewer()" title="Back (Esc)">&larr; Back</button>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].prevFile()" title="Previous (k)">&lsaquo; Prev</button>';tmpHTML+='<div class="retold-remote-viewer-title">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpFileName)+'</div>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" id="RetoldRemote-HeaderExploreBtn" onclick="pict.views[\'RetoldRemote-ImageExplorer\'].showExplorer(pict.AppData.RetoldRemote.CurrentViewerFile)" title="Explore image (e)" style="display:none;">&#128269; Explore</button>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\'].nextFile()" title="Next (j)">Next &rsaquo;</button>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._toggleFileInfo()" title="Info (i)">&#9432;</button>';tmpHTML+='<button class="retold-remote-viewer-nav-btn" onclick="pict.views[\'RetoldRemote-MediaViewer\'].toggleDistractionFree()" title="Distraction-Free (d)">&#9634;</button>';tmpHTML+='</div>';// Body with media content
11539
11838
  tmpHTML+='<div class="retold-remote-viewer-body">';// Exit hotspot for distraction-free mode (double-click/tap top-left to exit)
11540
11839
  let tmpDFHotspotDisplay=tmpRemote._distractionFreeMode?'':' style="display:none"';tmpHTML+='<div class="retold-remote-df-exit-hotspot" id="RetoldRemote-DF-ExitHotspot"'+tmpDFHotspotDisplay+'></div>';// For images, probe size first to decide what URL to use
11541
11840
  if(pMediaType==='image'){// Show a placeholder while we probe
@@ -11545,8 +11844,9 @@ tmpHTML+='</div>';// end viewer
11545
11844
  if(tmpViewerContainer){tmpViewerContainer.innerHTML=tmpHTML;}// For images, probe dimensions and decide how to display
11546
11845
  if(pMediaType==='image'){this._probeAndShowImage(pFilePath,tmpContentURL,tmpFileName);}// Fetch and populate file info
11547
11846
  this._loadFileInfo(pFilePath);// Fetch text content and initialize code viewer
11548
- if(pMediaType==='text'){this._loadCodeViewer(tmpContentURL,pFilePath);}// Load ebook viewer for epub/mobi
11549
- if(pMediaType==='document'){let tmpExt=pFilePath.replace(/^.*\./,'').toLowerCase();if(tmpExt==='epub'||tmpExt==='mobi'){this._loadEbookViewer(tmpContentURL,pFilePath);}}// Set up swipe navigation for touch devices
11847
+ if(pMediaType==='text'){this._loadCodeViewer(tmpContentURL,pFilePath);}// Load document viewers: ebook for epub/mobi, PDF for pdf, convert-then-PDF for others
11848
+ if(pMediaType==='document'){let tmpExt=pFilePath.replace(/^.*\./,'').toLowerCase();if(tmpExt==='epub'||tmpExt==='mobi'){this._loadEbookViewer(tmpContentURL,pFilePath);}else if(tmpExt==='pdf'){this._loadPdfViewer(tmpContentURL,pFilePath);}else{// Convertible document types convert to PDF first, then load PDF viewer
11849
+ this._loadConvertedDocumentViewer(pFilePath);}}// Set up swipe navigation for touch devices
11550
11850
  this._setupSwipeNavigation();// Set up distraction-free exit hotspot (double-click/tap top-left)
11551
11851
  this._setupDFExitHotspot();// Update topbar
11552
11852
  let tmpTopBar=this.pict.views['ContentEditor-TopBar'];if(tmpTopBar){tmpTopBar.updateInfo();}}/**
@@ -11622,10 +11922,9 @@ tmpSelf._insertImageTag(pContentURL,pFileName,false);});}/**
11622
11922
  * @param {number} [pOrigWidth] - Original image width (for the badge)
11623
11923
  * @param {number} [pOrigHeight]- Original image height (for the badge)
11624
11924
  */_insertImageTag(pURL,pFileName,pShowExplore,pOrigWidth,pOrigHeight){let tmpPlaceholder=document.getElementById('RetoldRemote-ImagePlaceholder');if(!tmpPlaceholder||!tmpPlaceholder.parentElement){return;}let tmpEscapedName=this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName);let tmpFragment=document.createDocumentFragment();// The image element
11625
- let tmpImg=document.createElement('img');tmpImg.src=pURL;tmpImg.alt=tmpEscapedName;tmpImg.id='RetoldRemote-ImageViewer-Img';tmpImg.style.cssText='max-width: 100%; max-height: 100%; object-fit: contain; cursor: zoom-in;';tmpImg.onload=function(){pict.views['RetoldRemote-ImageViewer'].initImage();};tmpImg.onclick=function(){pict.views['RetoldRemote-ImageViewer'].toggleZoom();};tmpFragment.appendChild(tmpImg);// Explore button (always present, initially hidden unless pShowExplore)
11626
- let tmpBtn=document.createElement('button');tmpBtn.className='retold-remote-image-explore-btn';tmpBtn.id='RetoldRemote-ImageExploreBtn';tmpBtn.style.display=pShowExplore?'':'none';tmpBtn.title='Open in deep-zoom explorer (e)';tmpBtn.innerHTML='&#128269; Explore';tmpBtn.onclick=function(){pict.views['RetoldRemote-ImageExplorer'].showExplorer(pict.AppData.RetoldRemote.CurrentViewerFile);};tmpFragment.appendChild(tmpBtn);// Dimension badge for large images
11627
- if(pShowExplore&&pOrigWidth&&pOrigHeight){let tmpBadge=document.createElement('div');tmpBadge.id='RetoldRemote-LargeImageBadge';tmpBadge.className='retold-remote-image-large-badge';tmpBadge.textContent=pOrigWidth+' \u00d7 '+pOrigHeight+' px (preview)';tmpFragment.appendChild(tmpBadge);}tmpPlaceholder.parentElement.replaceChild(tmpFragment,tmpPlaceholder);}_buildImageHTML(pURL,pFileName){let tmpEscapedName=this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName);let tmpHTML='<img src="'+pURL+'" alt="'+tmpEscapedName+'" '+'style="max-width: 100%; max-height: 100%; object-fit: contain; cursor: zoom-in;" '+'id="RetoldRemote-ImageViewer-Img" '+'onload="pict.views[\'RetoldRemote-ImageViewer\'].initImage()" '+'onclick="pict.views[\'RetoldRemote-ImageViewer\'].toggleZoom()">';// Explore button (hidden by default, shown by ImageViewer for large images)
11628
- tmpHTML+='<button class="retold-remote-image-explore-btn" id="RetoldRemote-ImageExploreBtn" '+'style="display:none" '+'onclick="pict.views[\'RetoldRemote-ImageExplorer\'].showExplorer(pict.AppData.RetoldRemote.CurrentViewerFile)" '+'title="Open in deep-zoom explorer (e)">'+'&#128269; Explore</button>';return tmpHTML;}_buildVideoHTML(pURL,pFileName){let tmpCapabilities=this.pict.AppData.RetoldRemote.ServerCapabilities||{};let tmpRemote=this.pict.AppData.RetoldRemote;let tmpFilePath=tmpRemote.CurrentViewerFile;// Build the action menu (shown by default instead of the player)
11925
+ let tmpImg=document.createElement('img');tmpImg.src=pURL;tmpImg.alt=tmpEscapedName;tmpImg.id='RetoldRemote-ImageViewer-Img';tmpImg.style.cssText='max-width: 100%; max-height: 100%; object-fit: contain; cursor: zoom-in;';tmpImg.onload=function(){pict.views['RetoldRemote-ImageViewer'].initImage();};tmpImg.onclick=function(){pict.views['RetoldRemote-ImageViewer'].toggleZoom();};tmpFragment.appendChild(tmpImg);// Always show the Explore button in the header nav bar for images
11926
+ let tmpHeaderExploreBtn=document.getElementById('RetoldRemote-HeaderExploreBtn');if(tmpHeaderExploreBtn){tmpHeaderExploreBtn.style.display='';}// Dimension badge for large images
11927
+ if(pShowExplore&&pOrigWidth&&pOrigHeight){let tmpBadge=document.createElement('div');tmpBadge.id='RetoldRemote-LargeImageBadge';tmpBadge.className='retold-remote-image-large-badge';tmpBadge.textContent=pOrigWidth+' \u00d7 '+pOrigHeight+' px (preview)';tmpFragment.appendChild(tmpBadge);}tmpPlaceholder.parentElement.replaceChild(tmpFragment,tmpPlaceholder);}_buildImageHTML(pURL,pFileName){let tmpEscapedName=this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName);let tmpHTML='<img src="'+pURL+'" alt="'+tmpEscapedName+'" '+'style="max-width: 100%; max-height: 100%; object-fit: contain; cursor: zoom-in;" '+'id="RetoldRemote-ImageViewer-Img" '+'onload="pict.views[\'RetoldRemote-ImageViewer\'].initImage()" '+'onclick="pict.views[\'RetoldRemote-ImageViewer\'].toggleZoom()">';return tmpHTML;}_buildVideoHTML(pURL,pFileName){let tmpCapabilities=this.pict.AppData.RetoldRemote.ServerCapabilities||{};let tmpRemote=this.pict.AppData.RetoldRemote;let tmpFilePath=tmpRemote.CurrentViewerFile;// Build the action menu (shown by default instead of the player)
11629
11928
  let tmpHTML='<div class="retold-remote-video-action-menu" id="RetoldRemote-VideoActionMenu">';tmpHTML+='<div class="retold-remote-video-action-menu-title">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName)+'</div>';// Frame preview container (loaded on demand via t key or automatically)
11630
11929
  if(tmpCapabilities.ffmpeg){tmpHTML+='<div id="RetoldRemote-VideoActionThumb" class="retold-remote-video-action-thumb-wrap"></div>';// Kick off frame extraction automatically
11631
11930
  setTimeout(()=>{this.loadVideoMenuFrame();},0);}// Explore option (e)
@@ -11645,18 +11944,28 @@ tmpRemote.VideoMenuActive=false;}/**
11645
11944
  if(tmpRemote.CurrentViewerFile!==tmpFilePath)return;let tmpWrap=document.getElementById('RetoldRemote-VideoActionThumb');if(!tmpWrap)return;if(pData&&pData.Frames&&pData.Frames.length>0){let tmpFrame=pData.Frames[0];let tmpFrameURL='/api/media/video-frame/'+pData.CacheKey+'/'+tmpFrame.Filename;tmpWrap.innerHTML='<img src="'+tmpFrameURL+'" '+'alt="'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(tmpFilePath.replace(/^.*\//,''))+'" '+'onerror="this.parentNode.innerHTML=\'\'">';}else{tmpWrap.innerHTML='';}}).catch(()=>{let tmpWrap=document.getElementById('RetoldRemote-VideoActionThumb');if(tmpWrap)tmpWrap.innerHTML='';});}_buildAudioHTML(pURL,pFileName){let tmpIconProvider=this.pict.providers['RetoldRemote-Icons'];let tmpIconHTML=tmpIconProvider?'<span class="retold-remote-icon retold-remote-icon-lg">'+tmpIconProvider.getIcon('music-note',64)+'</span>':'&#127925;';let tmpHTML='<div style="text-align: center; padding: 40px;">'+'<div style="margin-bottom: 24px;">'+tmpIconHTML+'</div>'+'<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName)+'</div>'+'<audio controls'+(this.pict.AppData.RetoldRemote.AutoplayAudio?' autoplay':'')+' preload="metadata" id="RetoldRemote-AudioPlayer" style="width: 100%; max-width: 500px;">'+'<source src="'+pURL+'">'+'Your browser does not support the audio tag.'+'</audio>';// Action buttons below the player
11646
11945
  tmpHTML+='<div style="margin-top: 20px; display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;">';// Explore Audio button (available when ffprobe is present)
11647
11946
  let tmpCapabilities=this.pict.AppData.RetoldRemote.ServerCapabilities||{};if(tmpCapabilities.ffprobe||tmpCapabilities.ffmpeg){tmpHTML+='<button class="retold-remote-explore-btn" '+'onclick="pict.views[\'RetoldRemote-AudioExplorer\'].showExplorer(pict.AppData.RetoldRemote.CurrentViewerFile)" '+'title="Explore waveform and extract segments from this audio">'+'Explore Audio'+'</button>';}// Stream with VLC
11648
- tmpHTML+='<button class="retold-remote-vlc-btn" '+'onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._streamWithVLC()" '+'title="Stream to VLC on this device (v)">'+'Stream with VLC'+'</button>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildDocumentHTML(pURL,pFileName,pFilePath){let tmpExtension=pFilePath.replace(/^.*\./,'').toLowerCase();if(tmpExtension==='pdf'){return'<iframe src="'+pURL+'" '+'style="width: 100%; height: 100%; border: none;">'+'</iframe>';}if(tmpExtension==='epub'||tmpExtension==='mobi'){return this._buildEbookHTML(pURL,pFileName,pFilePath);}// For other document types, show a download link
11947
+ tmpHTML+='<button class="retold-remote-vlc-btn" '+'onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._streamWithVLC()" '+'title="Stream to VLC on this device (v)">'+'Stream with VLC'+'</button>';tmpHTML+='</div>';tmpHTML+='</div>';return tmpHTML;}_buildDocumentHTML(pURL,pFileName,pFilePath){let tmpExtension=pFilePath.replace(/^.*\./,'').toLowerCase();if(tmpExtension==='pdf'){return this._buildPdfHTML(pURL,pFileName,pFilePath);}if(tmpExtension==='epub'||tmpExtension==='mobi'){return this._buildEbookHTML(pURL,pFileName,pFilePath);}// For convertible document types (doc, docx, rtf, odt, wpd, etc.),
11948
+ // show the PDF viewer shell — conversion happens async in _loadDocumentViewer
11949
+ let tmpConvertibleExts={'doc':true,'docx':true,'rtf':true,'odt':true,'wpd':true,'wps':true,'pages':true,'odp':true,'ppt':true,'pptx':true,'ods':true,'xls':true,'xlsx':true};if(tmpConvertibleExts[tmpExtension]){return this._buildPdfHTML(pURL,pFileName,pFilePath);}// Unknown document types: show a download link
11649
11950
  let tmpIconProvider=this.pict.providers['RetoldRemote-Icons'];let tmpDocIconHTML=tmpIconProvider?'<span class="retold-remote-icon retold-remote-icon-lg">'+tmpIconProvider.getIcon('document-large',64)+'</span>':'&#128196;';return'<div style="text-align: center; padding: 40px;">'+'<div style="margin-bottom: 24px;">'+tmpDocIconHTML+'</div>'+'<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName)+'</div>'+'<a href="'+pURL+'" target="_blank" style="color: var(--retold-accent); font-size: 0.9rem;">Open in new tab</a>'+'</div>';}// Note: _buildTextHTML, _getHighlightLanguage, _loadCodeViewer
11650
11951
  // are in MediaViewer-CodeViewer.js (mixed in below).
11651
11952
  // Note: _buildEbookHTML, _loadEbookViewer, _renderEpub, _renderEbookTOC,
11652
11953
  // ebookGoToChapter, ebookPrevPage, ebookNextPage, toggleEbookTOC
11653
11954
  // are in MediaViewer-EbookViewer.js (mixed in below).
11654
- _buildFallbackHTML(pURL,pFileName){let tmpIconProvider=this.pict.providers['RetoldRemote-Icons'];let tmpFallbackIconHTML=tmpIconProvider?'<span class="retold-remote-icon retold-remote-icon-lg">'+tmpIconProvider.getIcon('document-large',64)+'</span>':'&#128196;';return'<div style="text-align: center; padding: 40px;">'+'<div style="margin-bottom: 24px;">'+tmpFallbackIconHTML+'</div>'+'<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName)+'</div>'+'<a href="'+pURL+'" target="_blank" style="color: var(--retold-accent); font-size: 0.9rem;">Download / Open in new tab</a>'+'</div>';}/**
11955
+ /**
11956
+ * Convert a document (doc, docx, rtf, odt, wpd, etc.) to PDF and load in the PDF viewer.
11957
+ * Shows a converting message in the PDF content area while waiting.
11958
+ *
11959
+ * @param {string} pFilePath - Relative file path
11960
+ */_loadConvertedDocumentViewer(pFilePath){let tmpSelf=this;let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpPathParam=tmpProvider?tmpProvider._getPathParam(pFilePath):encodeURIComponent(pFilePath);// Show converting message in the PDF content area
11961
+ let tmpContent=document.getElementById('RetoldRemote-PdfContent');if(tmpContent){tmpContent.innerHTML='<div style="text-align:center;padding:40px;color:var(--retold-text-dim);">'+'<div style="font-size:1.5rem;margin-bottom:12px;">&#9881;</div>'+'Converting document to PDF\u2026'+'</div>';}fetch('/api/media/doc-convert?path='+tmpPathParam).then(pResponse=>pResponse.json()).then(pData=>{if(!pData||!pData.Success){throw new Error(pData?pData.Error:'Conversion failed.');}// Build the PDF URL from the ebook cache (same serving endpoint)
11962
+ let tmpPdfURL='/api/media/ebook/'+pData.CacheKey+'/'+pData.OutputFilename;// Load the PDF viewer with the converted file
11963
+ tmpSelf._loadPdfViewer(tmpPdfURL,pFilePath);}).catch(pError=>{if(tmpContent){let tmpFmt=tmpSelf.pict.providers['RetoldRemote-FormattingUtilities'];tmpContent.innerHTML='<div style="text-align:center;padding:40px;color:var(--retold-text-dim);">'+'<div style="margin-bottom:12px;color:#e06c75;">Conversion failed</div>'+'<div style="font-size:0.85rem;">'+tmpFmt.escapeHTML(pError.message)+'</div>'+'<div style="margin-top:16px;">'+'<a href="'+(tmpProvider?tmpProvider.getContentURL(pFilePath):'/content/'+encodeURIComponent(pFilePath))+'" target="_blank" style="color:var(--retold-accent);font-size:0.85rem;">Download original file</a>'+'</div>'+'</div>';}});}_buildFallbackHTML(pURL,pFileName){let tmpIconProvider=this.pict.providers['RetoldRemote-Icons'];let tmpFallbackIconHTML=tmpIconProvider?'<span class="retold-remote-icon retold-remote-icon-lg">'+tmpIconProvider.getIcon('document-large',64)+'</span>':'&#128196;';return'<div style="text-align: center; padding: 40px;">'+'<div style="margin-bottom: 24px;">'+tmpFallbackIconHTML+'</div>'+'<div style="font-size: 1.1rem; color: var(--retold-text-secondary); margin-bottom: 24px;">'+this.pict.providers['RetoldRemote-FormattingUtilities'].escapeHTML(pFileName)+'</div>'+'<a href="'+pURL+'" target="_blank" style="color: var(--retold-accent); font-size: 0.9rem;">Download / Open in new tab</a>'+'</div>';}/**
11655
11964
  * Fetch file info and populate the overlay and video stats bar.
11656
11965
  */_loadFileInfo(pFilePath){let tmpSelf=this;let tmpProvider=this.pict.providers['RetoldRemote-Provider'];if(!tmpProvider){return;}tmpProvider.fetchMediaProbe(pFilePath,(pError,pData)=>{if(!pData){return;}// Populate the info overlay
11657
11966
  let tmpOverlay=document.getElementById('RetoldRemote-FileInfo-Overlay');if(tmpOverlay){let tmpHTML='';if(pData.Size!==undefined){tmpHTML+='<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Size</span><span class="retold-remote-fileinfo-value">'+tmpSelf.pict.providers['RetoldRemote-FormattingUtilities'].formatFileSize(pData.Size)+'</span></div>';}if(pData.Width&&pData.Height){tmpHTML+='<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Dimensions</span><span class="retold-remote-fileinfo-value">'+pData.Width+' x '+pData.Height+'</span></div>';}if(pData.Duration){let tmpMin=Math.floor(pData.Duration/60);let tmpSec=Math.floor(pData.Duration%60);tmpHTML+='<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Duration</span><span class="retold-remote-fileinfo-value">'+tmpMin+':'+(tmpSec<10?'0':'')+tmpSec+'</span></div>';}if(pData.Codec){tmpHTML+='<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Codec</span><span class="retold-remote-fileinfo-value">'+pData.Codec+'</span></div>';}if(pData.Format){tmpHTML+='<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Format</span><span class="retold-remote-fileinfo-value">'+pData.Format+'</span></div>';}if(pData.Modified){tmpHTML+='<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Modified</span><span class="retold-remote-fileinfo-value">'+new Date(pData.Modified).toLocaleString()+'</span></div>';}if(pData.Path){tmpHTML+='<div class="retold-remote-fileinfo-row"><span class="retold-remote-fileinfo-label">Path</span><span class="retold-remote-fileinfo-value">'+pData.Path+'</span></div>';}tmpOverlay.innerHTML=tmpHTML;}// Populate the video stats bar (if viewing a video)
11658
11967
  let tmpStatsBar=document.getElementById('RetoldRemote-VideoStats');if(tmpStatsBar){let tmpStatsHTML='';if(pData.Duration){let tmpMin=Math.floor(pData.Duration/60);let tmpSec=Math.floor(pData.Duration%60);tmpStatsHTML+='<span><span class="retold-remote-video-stat-label">Duration</span> <span class="retold-remote-video-stat-value">'+tmpMin+':'+(tmpSec<10?'0':'')+tmpSec+'</span></span>';}if(pData.Width&&pData.Height){tmpStatsHTML+='<span><span class="retold-remote-video-stat-label">Resolution</span> <span class="retold-remote-video-stat-value">'+pData.Width+'×'+pData.Height+'</span></span>';}if(pData.Codec){tmpStatsHTML+='<span><span class="retold-remote-video-stat-label">Codec</span> <span class="retold-remote-video-stat-value">'+pData.Codec+'</span></span>';}if(pData.Bitrate){let tmpBitrate=pData.Bitrate;let tmpBitrateStr;if(tmpBitrate>=1000000){tmpBitrateStr=(tmpBitrate/1000000).toFixed(1)+' Mbps';}else if(tmpBitrate>=1000){tmpBitrateStr=Math.round(tmpBitrate/1000)+' kbps';}else{tmpBitrateStr=tmpBitrate+' bps';}tmpStatsHTML+='<span><span class="retold-remote-video-stat-label">Bitrate</span> <span class="retold-remote-video-stat-value">'+tmpBitrateStr+'</span></span>';}if(pData.Size!==undefined){tmpStatsHTML+='<span><span class="retold-remote-video-stat-label">Size</span> <span class="retold-remote-video-stat-value">'+tmpSelf.pict.providers['RetoldRemote-FormattingUtilities'].formatFileSize(pData.Size)+'</span></span>';}// Preserve the Explore and VLC buttons if they exist
11659
- let tmpExploreBtn=tmpStatsBar.querySelector('.retold-remote-explore-btn');let tmpExploreHTML=tmpExploreBtn?tmpExploreBtn.outerHTML:'';let tmpVLCBtn=tmpStatsBar.querySelector('.retold-remote-vlc-btn');let tmpVLCHTML=tmpVLCBtn?tmpVLCBtn.outerHTML:'';tmpStatsBar.innerHTML=tmpStatsHTML+tmpExploreHTML+tmpVLCHTML;}});}}Object.assign(RetoldRemoteMediaViewerView.prototype,_MediaViewerEbookViewer);Object.assign(RetoldRemoteMediaViewerView.prototype,_MediaViewerCodeViewer);RetoldRemoteMediaViewerView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteMediaViewerView;},{"./MediaViewer-CodeViewer":137,"./MediaViewer-EbookViewer":138,"pict-view":88}],147:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-SettingsPanel",DefaultRenderable:"RetoldRemote-SettingsPanel",DefaultDestinationAddress:"#RetoldRemote-Settings-Container",AutoRender:false,CSS:``};class RetoldRemoteSettingsPanelView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);}onAfterRender(){super.onAfterRender();this._renderSettingsContent();}_renderSettingsContent(){let tmpContainer=document.getElementById('RetoldRemote-Settings-Container');if(!tmpContainer){return;}let tmpRemote=this.pict.AppData.RetoldRemote;let tmpCapabilities=tmpRemote.ServerCapabilities||{};let tmpHTML='<div class="retold-remote-settings">';// Appearance section (theme dropdown)
11968
+ let tmpExploreBtn=tmpStatsBar.querySelector('.retold-remote-explore-btn');let tmpExploreHTML=tmpExploreBtn?tmpExploreBtn.outerHTML:'';let tmpVLCBtn=tmpStatsBar.querySelector('.retold-remote-vlc-btn');let tmpVLCHTML=tmpVLCBtn?tmpVLCBtn.outerHTML:'';tmpStatsBar.innerHTML=tmpStatsHTML+tmpExploreHTML+tmpVLCHTML;}});}}Object.assign(RetoldRemoteMediaViewerView.prototype,_MediaViewerEbookViewer);Object.assign(RetoldRemoteMediaViewerView.prototype,_MediaViewerCodeViewer);Object.assign(RetoldRemoteMediaViewerView.prototype,_MediaViewerPdfViewer);RetoldRemoteMediaViewerView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteMediaViewerView;},{"./MediaViewer-CodeViewer":137,"./MediaViewer-EbookViewer":138,"./MediaViewer-PdfViewer":139,"pict-view":88}],148:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-SettingsPanel",DefaultRenderable:"RetoldRemote-SettingsPanel",DefaultDestinationAddress:"#RetoldRemote-Settings-Container",AutoRender:false,CSS:``};class RetoldRemoteSettingsPanelView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);}onAfterRender(){super.onAfterRender();this._renderSettingsContent();}_renderSettingsContent(){let tmpContainer=document.getElementById('RetoldRemote-Settings-Container');if(!tmpContainer){return;}let tmpRemote=this.pict.AppData.RetoldRemote;let tmpCapabilities=tmpRemote.ServerCapabilities||{};let tmpHTML='<div class="retold-remote-settings">';// Appearance section (theme dropdown)
11660
11969
  tmpHTML+='<div class="retold-remote-settings-section">';tmpHTML+='<div class="retold-remote-settings-section-title">Appearance</div>';tmpHTML+='<div class="retold-remote-settings-row">';tmpHTML+='<span class="retold-remote-settings-label">Theme</span>';tmpHTML+='<select class="retold-remote-settings-select" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeTheme(this.value)">';let tmpThemeProvider=this.pict.providers['RetoldRemote-Theme'];if(tmpThemeProvider){let tmpThemes=tmpThemeProvider.getThemeList();let tmpCurrentTheme=tmpThemeProvider.getCurrentTheme();let tmpCurrentCategory='';for(let i=0;i<tmpThemes.length;i++){let tmpTheme=tmpThemes[i];if(tmpTheme.category!==tmpCurrentCategory){if(tmpCurrentCategory){tmpHTML+='</optgroup>';}tmpHTML+='<optgroup label="'+tmpTheme.category+'">';tmpCurrentCategory=tmpTheme.category;}tmpHTML+='<option value="'+tmpTheme.key+'"'+(tmpTheme.key===tmpCurrentTheme?' selected':'')+'>'+tmpTheme.name+'</option>';}if(tmpCurrentCategory){tmpHTML+='</optgroup>';}}tmpHTML+='</select>';tmpHTML+='</div>';tmpHTML+='</div>';// end appearance section
11661
11970
  // Gallery section
11662
11971
  tmpHTML+='<div class="retold-remote-settings-section">';tmpHTML+='<div class="retold-remote-settings-section-title">Gallery</div>';// View mode
@@ -11709,7 +12018,52 @@ if(pKey==='NamingTemplate'){this._renderSettingsContent();}}/**
11709
12018
  * @returns {string}
11710
12019
  */_escapeHTML(pStr){if(!pStr)return'';return String(pStr).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}/**
11711
12020
  * Re-run the filter/sort pipeline and refresh the gallery.
11712
- */_refilterGallery(){let tmpRemote=this.pict.AppData.RetoldRemote;let tmpFilterSort=this.pict.providers['RetoldRemote-GalleryFilterSort'];if(tmpFilterSort){tmpFilterSort.runFilterPipeline();}if(tmpRemote.ActiveMode==='gallery'){let tmpGalleryView=this.pict.views['RetoldRemote-Gallery'];if(tmpGalleryView){tmpGalleryView.renderGallery();}}}}RetoldRemoteSettingsPanelView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteSettingsPanelView;},{"pict-view":88}],148:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"ContentEditor-TopBar",DefaultRenderable:"RetoldRemote-TopBar",DefaultDestinationAddress:"#ContentEditor-TopBar-Container",AutoRender:false,CSS:``,Templates:[{Hash:"RetoldRemote-TopBar",Template:/*html*/`
12021
+ */_refilterGallery(){let tmpRemote=this.pict.AppData.RetoldRemote;let tmpFilterSort=this.pict.providers['RetoldRemote-GalleryFilterSort'];if(tmpFilterSort){tmpFilterSort.runFilterPipeline();}if(tmpRemote.ActiveMode==='gallery'){let tmpGalleryView=this.pict.views['RetoldRemote-Gallery'];if(tmpGalleryView){tmpGalleryView.renderGallery();}}}}RetoldRemoteSettingsPanelView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteSettingsPanelView;},{"pict-view":88}],149:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-SubimagesPanel",DefaultRenderable:"RetoldRemote-SubimagesPanel",DefaultDestinationAddress:"#RetoldRemote-Subimages-Container",AutoRender:false,CSS:``};/**
12022
+ * Subimages Panel — sidebar tab showing labeled subimage regions
12023
+ * for the currently viewed image file.
12024
+ *
12025
+ * Regions are fetched from the SubimageService API and displayed
12026
+ * as a list with label, dimensions, and action buttons.
12027
+ */class RetoldRemoteSubimagesPanelView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._regions=[];this._currentPath='';}/**
12028
+ * Render the subimages panel for the current file.
12029
+ */render(){let tmpContainer=document.getElementById('RetoldRemote-Subimages-Container');if(!tmpContainer){return;}let tmpRemote=this.pict.AppData.RetoldRemote;let tmpFilePath=tmpRemote.CurrentViewerFile;let tmpMediaType=tmpRemote.CurrentViewerMediaType;// Show for images and documents (EPUB, PDF, CBZ pages)
12030
+ if(!tmpFilePath){tmpContainer.innerHTML='<div style="padding:12px;color:var(--retold-text-dim);font-size:0.82rem;">View a file to see its regions.</div>';this._regions=[];this._currentPath='';return;}// If the file changed, fetch regions
12031
+ if(tmpFilePath!==this._currentPath){this._currentPath=tmpFilePath;this._fetchAndRender(tmpFilePath,tmpContainer);return;}// Re-render with cached regions (e.g. after add/delete)
12032
+ this._renderRegionList(tmpContainer);}/**
12033
+ * Force a refresh from the server for the current file.
12034
+ */refresh(){this._currentPath='';this.render();}/**
12035
+ * Fetch regions from the server and render.
12036
+ *
12037
+ * @param {string} pFilePath - Relative file path
12038
+ * @param {HTMLElement} pContainer - The container to render into
12039
+ */_fetchAndRender(pFilePath,pContainer){let tmpSelf=this;let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpPathParam=tmpProvider?tmpProvider._getPathParam(pFilePath):encodeURIComponent(pFilePath);pContainer.innerHTML='<div style="padding:12px;color:var(--retold-text-dim);font-size:0.82rem;">Loading\u2026</div>';fetch('/api/media/subimage-regions?path='+tmpPathParam).then(pResponse=>pResponse.json()).then(pResult=>{if(pResult&&pResult.Success){tmpSelf._regions=pResult.Regions||[];}else{tmpSelf._regions=[];}tmpSelf._renderRegionList(pContainer);}).catch(()=>{tmpSelf._regions=[];tmpSelf._renderRegionList(pContainer);});}/**
12040
+ * Render the region list into the container.
12041
+ *
12042
+ * @param {HTMLElement} pContainer - The container element
12043
+ */_renderRegionList(pContainer){if(!pContainer){pContainer=document.getElementById('RetoldRemote-Subimages-Container');}if(!pContainer){return;}// Also sync the regions to the image explorer if it's open
12044
+ let tmpIEX=this.pict.views['RetoldRemote-ImageExplorer'];if(tmpIEX&&tmpIEX._currentPath===this._currentPath){tmpIEX._savedRegions=this._regions;}let tmpFmt=this.pict.providers['RetoldRemote-FormattingUtilities'];let tmpFileName=(this._currentPath||'').replace(/^.*\//,'');let tmpHTML='<div class="retold-remote-subimages-panel">';// Header
12045
+ tmpHTML+='<div style="padding:8px 10px;font-size:0.78rem;color:var(--retold-text-dim);border-bottom:1px solid var(--retold-border);">';tmpHTML+=tmpFmt.escapeHTML(tmpFileName);tmpHTML+=' &mdash; '+this._regions.length+' region'+(this._regions.length!==1?'s':'');tmpHTML+='</div>';if(this._regions.length===0){tmpHTML+='<div style="padding:16px 12px;color:var(--retold-text-dim);font-size:0.8rem;text-align:center;">';tmpHTML+='No regions yet.<br>Use selection tools in the viewer to create regions.';tmpHTML+='</div>';}else{for(let i=0;i<this._regions.length;i++){let tmpRegion=this._regions[i];let tmpLabel=tmpRegion.Label||'(unlabeled)';let tmpIsText=tmpRegion.Type==='text-selection';// Build description based on region type
12046
+ let tmpDesc='';if(tmpIsText){let tmpPreview=(tmpRegion.SelectedText||'').substring(0,60);if((tmpRegion.SelectedText||'').length>60)tmpPreview+='\u2026';tmpDesc=tmpPreview||'(no text)';if(tmpRegion.PageNumber){tmpDesc='p.'+tmpRegion.PageNumber+' \u2014 '+tmpDesc;}else if(tmpRegion.ChapterTitle){tmpDesc=tmpRegion.ChapterTitle;}}else{tmpDesc=(tmpRegion.Width||0)+' \u00d7 '+(tmpRegion.Height||0)+' px';if(tmpRegion.PageNumber){tmpDesc='p.'+tmpRegion.PageNumber+' \u2014 '+tmpDesc;}else if(tmpRegion.X!==null&&tmpRegion.Y!==null){tmpDesc+=' at '+tmpRegion.X+', '+tmpRegion.Y;}}// Type icon
12047
+ let tmpTypeIcon=tmpIsText?'\uD83D\uDCDD':'\uD83D\uDD32';tmpHTML+='<div class="retold-remote-subimages-item" data-region-id="'+tmpRegion.ID+'">';// Region info
12048
+ tmpHTML+='<div class="retold-remote-subimages-item-info">';tmpHTML+='<div class="retold-remote-subimages-item-label">'+tmpTypeIcon+' '+tmpFmt.escapeHTML(tmpLabel)+'</div>';tmpHTML+='<div class="retold-remote-subimages-item-dims">'+tmpFmt.escapeHTML(tmpDesc)+'</div>';tmpHTML+='</div>';// Actions
12049
+ tmpHTML+='<div class="retold-remote-subimages-item-actions">';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-SubimagesPanel\'].navigateToRegion(\''+tmpRegion.ID+'\')" title="Navigate to region" style="font-size:0.7rem;padding:2px 6px;">&#128270;</button>';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-SubimagesPanel\'].addRegionToCollection(\''+tmpRegion.ID+'\')" title="Add to collection" style="font-size:0.7rem;padding:2px 6px;">&#10010;</button>';tmpHTML+='<button onclick="pict.views[\'RetoldRemote-SubimagesPanel\'].deleteRegion(\''+tmpRegion.ID+'\')" title="Delete region" style="font-size:0.7rem;padding:2px 6px;">&#128465;</button>';tmpHTML+='</div>';tmpHTML+='</div>';}}tmpHTML+='</div>';pContainer.innerHTML=tmpHTML;}/**
12050
+ * Navigate to a specific region — handles images, EPUB, and PDF.
12051
+ *
12052
+ * @param {string} pRegionID - The region ID
12053
+ */navigateToRegion(pRegionID){let tmpRegion=null;for(let i=0;i<this._regions.length;i++){if(this._regions[i].ID===pRegionID){tmpRegion=this._regions[i];break;}}if(!tmpRegion){return;}let tmpRemote=this.pict.AppData.RetoldRemote;let tmpMediaViewer=this.pict.views['RetoldRemote-MediaViewer'];// EPUB: navigate to CFI location
12054
+ if(tmpRegion.CFI&&tmpMediaViewer&&tmpMediaViewer._activeRendition){tmpMediaViewer._activeRendition.display(tmpRegion.CFI);return;}// PDF: navigate to page
12055
+ if(tmpRegion.PageNumber&&tmpMediaViewer&&typeof tmpMediaViewer._renderPdfPage==='function'){tmpMediaViewer._renderPdfPage(tmpRegion.PageNumber);return;}// Image: use image explorer
12056
+ let tmpIEX=this.pict.views['RetoldRemote-ImageExplorer'];if(tmpRemote.ActiveMode==='image-explorer'&&tmpIEX){tmpIEX.zoomToRegion(pRegionID);}else if(tmpIEX&&this._currentPath){tmpIEX.showExplorer(this._currentPath);setTimeout(()=>{tmpIEX.zoomToRegion(pRegionID);},800);}}/**
12057
+ * Add a subimage region to the active collection.
12058
+ *
12059
+ * @param {string} pRegionID - The region ID to add
12060
+ */addRegionToCollection(pRegionID){let tmpRegion=null;for(let i=0;i<this._regions.length;i++){if(this._regions[i].ID===pRegionID){tmpRegion=this._regions[i];break;}}if(!tmpRegion){return;}let tmpCollMgr=this.pict.providers['RetoldRemote-CollectionManager'];if(tmpCollMgr){let tmpGUID=tmpCollMgr.getQuickAddTargetGUID();if(tmpGUID){tmpCollMgr.addSubimageToCollection(tmpGUID,tmpRegion,this._currentPath);}else{let tmpTopBar=this.pict.views['ContentEditor-TopBar'];if(tmpTopBar&&typeof tmpTopBar.showAddToCollectionDropdown==='function'){tmpTopBar.showAddToCollectionDropdown();}}}}/**
12061
+ * Delete a subimage region.
12062
+ *
12063
+ * @param {string} pRegionID - The region ID to delete
12064
+ */deleteRegion(pRegionID){let tmpIEX=this.pict.views['RetoldRemote-ImageExplorer'];if(tmpIEX&&tmpIEX._currentPath===this._currentPath){// Delegate to the explorer which handles the API call and overlay removal
12065
+ tmpIEX.deleteRegion(pRegionID);}else{// Delete directly via API
12066
+ let tmpSelf=this;let tmpProvider=this.pict.providers['RetoldRemote-Provider'];let tmpPathParam=tmpProvider?tmpProvider._getPathParam(this._currentPath):encodeURIComponent(this._currentPath);fetch('/api/media/subimage-regions/'+encodeURIComponent(pRegionID)+'?path='+tmpPathParam,{method:'DELETE'}).then(pResponse=>pResponse.json()).then(pResult=>{if(pResult&&pResult.Success){tmpSelf._regions=pResult.Regions||[];tmpSelf._renderRegionList();let tmpToast=tmpSelf.pict.providers['RetoldRemote-ToastNotification'];if(tmpToast){tmpToast.showToast('Region deleted');}}}).catch(()=>{});}}}RetoldRemoteSubimagesPanelView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteSubimagesPanelView;},{"pict-view":88}],150:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"ContentEditor-TopBar",DefaultRenderable:"RetoldRemote-TopBar",DefaultDestinationAddress:"#ContentEditor-TopBar-Container",AutoRender:false,CSS:``,Templates:[{Hash:"RetoldRemote-TopBar",Template:/*html*/`
11713
12067
  <div class="retold-remote-topbar">
11714
12068
  <button class="retold-remote-topbar-sidebar-toggle" id="RetoldRemote-TopBar-SidebarToggle" onclick="pict.views['ContentEditor-Layout'].toggleSidebar()" title="Toggle Sidebar"></button>
11715
12069
  <button class="retold-remote-topbar-df-toggle" id="RetoldRemote-TopBar-DFToggle" onclick="pict.views['ContentEditor-TopBar'].toggleDistractionFree()" title="Distraction-free mode (d)"></button>
@@ -11820,7 +12174,7 @@ tmpBtn.style.position='relative';tmpBtn.appendChild(tmpDropdown);// Close on out
11820
12174
  setTimeout(()=>{document.addEventListener('click',tmpSelf._boundCloseDropdown=pClickEvent=>{if(!tmpDropdown.contains(pClickEvent.target)&&pClickEvent.target!==tmpBtn){tmpSelf._closeAddToCollectionDropdown();}});},10);});}/**
11821
12175
  * Close the add-to-collection dropdown.
11822
12176
  */_closeAddToCollectionDropdown(){let tmpDropdown=document.getElementById('RetoldRemote-AddToCollection-Dropdown');if(tmpDropdown){tmpDropdown.remove();}if(this._boundCloseDropdown){document.removeEventListener('click',this._boundCloseDropdown);this._boundCloseDropdown=null;}// Clear any pending clip context that was never consumed
11823
- let tmpManager=this.pict.providers['RetoldRemote-CollectionManager'];if(tmpManager){tmpManager.clearPendingClipContext();}}}RetoldRemoteTopBarView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteTopBarView;},{"pict-view":88}],149:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-VLCSetup",DefaultRenderable:"RetoldRemote-VLCSetup",DefaultDestinationAddress:"#ContentEditor-Application-Container",AutoRender:false,CSS:``};class RetoldRemoteVLCSetupView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._activePlatformTab=this._detectPlatform();this._modalVisible=false;this._boundKeyHandler=null;}_detectPlatform(){let tmpUA=typeof navigator!=='undefined'?navigator.userAgent:'';if(/iPhone|iPad|iPod/i.test(tmpUA)){return'ios';}// iPadOS 13+ sends a macOS user agent — detect via maxTouchPoints
12177
+ let tmpManager=this.pict.providers['RetoldRemote-CollectionManager'];if(tmpManager){tmpManager.clearPendingClipContext();}}}RetoldRemoteTopBarView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteTopBarView;},{"pict-view":88}],151:[function(require,module,exports){const libPictView=require('pict-view');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-VLCSetup",DefaultRenderable:"RetoldRemote-VLCSetup",DefaultDestinationAddress:"#ContentEditor-Application-Container",AutoRender:false,CSS:``};class RetoldRemoteVLCSetupView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._activePlatformTab=this._detectPlatform();this._modalVisible=false;this._boundKeyHandler=null;}_detectPlatform(){let tmpUA=typeof navigator!=='undefined'?navigator.userAgent:'';if(/iPhone|iPad|iPod/i.test(tmpUA)){return'ios';}// iPadOS 13+ sends a macOS user agent — detect via maxTouchPoints
11824
12178
  if(/Macintosh/i.test(tmpUA)&&typeof navigator!=='undefined'&&navigator.maxTouchPoints>1){return'ios';}if(/Android/i.test(tmpUA)){return'android';}if(/Macintosh|Mac OS X/.test(tmpUA)){return'macos';}if(/Windows/.test(tmpUA)){return'windows';}return'linux';}openModal(){if(this._modalVisible){return;}this._modalVisible=true;// Create the backdrop
11825
12179
  let tmpBackdrop=document.createElement('div');tmpBackdrop.className='retold-remote-vlc-modal-backdrop';tmpBackdrop.id='RetoldRemote-VLCSetup-Backdrop';tmpBackdrop.onclick=pEvent=>{if(pEvent.target===tmpBackdrop){this.closeModal();}};// Create the modal
11826
12180
  let tmpModal=document.createElement('div');tmpModal.className='retold-remote-vlc-modal';// Header
@@ -11840,7 +12194,7 @@ return["Windows Registry Editor Version 5.00","","[HKEY_CLASSES_ROOT\\vlc]","@=\
11840
12194
  // protocol to use it. The handler URL-decodes the argument because
11841
12195
  // the client percent-encodes the URL to prevent Windows from
11842
12196
  // stripping colons in nested http:// URLs.
11843
- return["@echo off","REM VLC Protocol Handler Setup for Windows","REM Run this as Administrator","","REM Create the handler directory","mkdir \"%APPDATA%\\VLCProtocol\" 2>nul","","REM Write the PowerShell handler script","(","echo $url = $args[0]","echo if ^($url -and $url.StartsWith^('vlc://'^)^) { $url = $url.Substring^(6^) }","echo $url = [System.Uri]::UnescapeDataString^($url^)","echo $url = $url.TrimEnd^('/'^)","echo if ^($url^) { Start-Process 'C:\\Program Files\\VideoLAN\\VLC\\vlc.exe' -ArgumentList $url }",") > \"%APPDATA%\\VLCProtocol\\handler.ps1\"","","REM Register the protocol in the registry","reg add \"HKCU\\Software\\Classes\\vlc\" /ve /d \"URL:VLC Protocol\" /f","reg add \"HKCU\\Software\\Classes\\vlc\" /v \"URL Protocol\" /d \"\" /f","reg add \"HKCU\\Software\\Classes\\vlc\\shell\\open\\command\" /ve /d \"powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File \\\"%APPDATA%\\VLCProtocol\\handler.ps1\\\" \\\"%%1\\\"\" /f","","echo VLC protocol handler installed successfully.","pause"].join('\n');}_getLinuxSetupScript(){return["# Create handler script","mkdir -p ~/.local/bin","cat > ~/.local/bin/vlc-protocol << 'EOF'","#!/bin/bash","URL=\"$1\"","URL=\"${URL#vlc://}\"","URL=$(python3 -c \"import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))\" \"$URL\")","exec vlc \"$URL\" &","EOF","chmod +x ~/.local/bin/vlc-protocol","","# Create .desktop file","cat > ~/.local/share/applications/vlc-protocol.desktop << 'EOF'","[Desktop Entry]","Name=VLC Protocol Handler","Exec=bash -c '~/.local/bin/vlc-protocol %u'","Type=Application","NoDisplay=true","MimeType=x-scheme-handler/vlc;","EOF","","# Register the handler","xdg-mime default vlc-protocol.desktop x-scheme-handler/vlc","update-desktop-database ~/.local/share/applications/","","echo \"VLC protocol handler installed successfully.\""].join('\n');}_copyToClipboard(pText,pLabel){if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(pText).then(()=>{this.pict.providers['RetoldRemote-ToastNotification'].showToast(pLabel+' copied to clipboard');}).catch(()=>{this._fallbackCopy(pText,pLabel);});}else{this._fallbackCopy(pText,pLabel);}}_fallbackCopy(pText,pLabel){let tmpTextarea=document.createElement('textarea');tmpTextarea.value=pText;tmpTextarea.style.position='fixed';tmpTextarea.style.left='-9999px';document.body.appendChild(tmpTextarea);tmpTextarea.select();try{document.execCommand('copy');this.pict.providers['RetoldRemote-ToastNotification'].showToast(pLabel+' copied to clipboard');}catch(pErr){this.pict.providers['RetoldRemote-ToastNotification'].showToast('Failed to copy - please select and copy manually');}document.body.removeChild(tmpTextarea);}copyMacSetup(){this._copyToClipboard(this._getMacSetupScript(),'macOS setup script');}copyWindowsReg(){this._copyToClipboard(this._getWindowsRegFile(),'Registry file');}copyWindowsBatch(){this._copyToClipboard(this._getWindowsBatchScript(),'Batch script');}copyLinuxSetup(){this._copyToClipboard(this._getLinuxSetupScript(),'Linux setup script');}testProtocol(){let tmpIsWindows=/Windows/.test(navigator.userAgent);let tmpIsMobile=/iPhone|iPad|iPod|Android/i.test(navigator.userAgent);let tmpSampleURL='https://www.sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4';let tmpTestURL=tmpIsWindows||tmpIsMobile?'vlc://'+tmpSampleURL:'vlc://'+encodeURIComponent(tmpSampleURL);let tmpLink=document.createElement('a');tmpLink.href=tmpTestURL;tmpLink.style.display='none';document.body.appendChild(tmpLink);tmpLink.click();document.body.removeChild(tmpLink);}}RetoldRemoteVLCSetupView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteVLCSetupView;},{"pict-view":88}],150:[function(require,module,exports){const libPictView=require('pict-view');const _VideoExplorerSelection=require('./VideoExplorer-Selection');const _VideoExplorerCustomFrames=require('./VideoExplorer-CustomFrames');const _VideoExplorerPreview=require('./VideoExplorer-Preview');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-VideoExplorer",DefaultRenderable:"RetoldRemote-VideoExplorer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteVideoExplorerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._currentPath='';this._frameData=null;this._selectedFrameIndex=-1;this._frameCount=20;this._fullResFrames=true;this._customFrames=[];// Selection mode and state for timeline range selection
12197
+ return["@echo off","REM VLC Protocol Handler Setup for Windows","REM Run this as Administrator","","REM Create the handler directory","mkdir \"%APPDATA%\\VLCProtocol\" 2>nul","","REM Write the PowerShell handler script","(","echo $url = $args[0]","echo if ^($url -and $url.StartsWith^('vlc://'^)^) { $url = $url.Substring^(6^) }","echo $url = [System.Uri]::UnescapeDataString^($url^)","echo $url = $url.TrimEnd^('/'^)","echo if ^($url^) { Start-Process 'C:\\Program Files\\VideoLAN\\VLC\\vlc.exe' -ArgumentList $url }",") > \"%APPDATA%\\VLCProtocol\\handler.ps1\"","","REM Register the protocol in the registry","reg add \"HKCU\\Software\\Classes\\vlc\" /ve /d \"URL:VLC Protocol\" /f","reg add \"HKCU\\Software\\Classes\\vlc\" /v \"URL Protocol\" /d \"\" /f","reg add \"HKCU\\Software\\Classes\\vlc\\shell\\open\\command\" /ve /d \"powershell.exe -NoProfile -WindowStyle Hidden -ExecutionPolicy Bypass -File \\\"%APPDATA%\\VLCProtocol\\handler.ps1\\\" \\\"%%1\\\"\" /f","","echo VLC protocol handler installed successfully.","pause"].join('\n');}_getLinuxSetupScript(){return["# Create handler script","mkdir -p ~/.local/bin","cat > ~/.local/bin/vlc-protocol << 'EOF'","#!/bin/bash","URL=\"$1\"","URL=\"${URL#vlc://}\"","URL=$(python3 -c \"import sys, urllib.parse; print(urllib.parse.unquote(sys.argv[1]))\" \"$URL\")","exec vlc \"$URL\" &","EOF","chmod +x ~/.local/bin/vlc-protocol","","# Create .desktop file","cat > ~/.local/share/applications/vlc-protocol.desktop << 'EOF'","[Desktop Entry]","Name=VLC Protocol Handler","Exec=bash -c '~/.local/bin/vlc-protocol %u'","Type=Application","NoDisplay=true","MimeType=x-scheme-handler/vlc;","EOF","","# Register the handler","xdg-mime default vlc-protocol.desktop x-scheme-handler/vlc","update-desktop-database ~/.local/share/applications/","","echo \"VLC protocol handler installed successfully.\""].join('\n');}_copyToClipboard(pText,pLabel){if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(pText).then(()=>{this.pict.providers['RetoldRemote-ToastNotification'].showToast(pLabel+' copied to clipboard');}).catch(()=>{this._fallbackCopy(pText,pLabel);});}else{this._fallbackCopy(pText,pLabel);}}_fallbackCopy(pText,pLabel){let tmpTextarea=document.createElement('textarea');tmpTextarea.value=pText;tmpTextarea.style.position='fixed';tmpTextarea.style.left='-9999px';document.body.appendChild(tmpTextarea);tmpTextarea.select();try{document.execCommand('copy');this.pict.providers['RetoldRemote-ToastNotification'].showToast(pLabel+' copied to clipboard');}catch(pErr){this.pict.providers['RetoldRemote-ToastNotification'].showToast('Failed to copy - please select and copy manually');}document.body.removeChild(tmpTextarea);}copyMacSetup(){this._copyToClipboard(this._getMacSetupScript(),'macOS setup script');}copyWindowsReg(){this._copyToClipboard(this._getWindowsRegFile(),'Registry file');}copyWindowsBatch(){this._copyToClipboard(this._getWindowsBatchScript(),'Batch script');}copyLinuxSetup(){this._copyToClipboard(this._getLinuxSetupScript(),'Linux setup script');}testProtocol(){let tmpIsWindows=/Windows/.test(navigator.userAgent);let tmpIsMobile=/iPhone|iPad|iPod|Android/i.test(navigator.userAgent);let tmpSampleURL='https://www.sample-videos.com/video321/mp4/720/big_buck_bunny_720p_1mb.mp4';let tmpTestURL=tmpIsWindows||tmpIsMobile?'vlc://'+tmpSampleURL:'vlc://'+encodeURIComponent(tmpSampleURL);let tmpLink=document.createElement('a');tmpLink.href=tmpTestURL;tmpLink.style.display='none';document.body.appendChild(tmpLink);tmpLink.click();document.body.removeChild(tmpLink);}}RetoldRemoteVLCSetupView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteVLCSetupView;},{"pict-view":88}],152:[function(require,module,exports){const libPictView=require('pict-view');const _VideoExplorerSelection=require('./VideoExplorer-Selection');const _VideoExplorerCustomFrames=require('./VideoExplorer-CustomFrames');const _VideoExplorerPreview=require('./VideoExplorer-Preview');const _ViewConfiguration={ViewIdentifier:"RetoldRemote-VideoExplorer",DefaultRenderable:"RetoldRemote-VideoExplorer",DefaultDestinationAddress:"#RetoldRemote-Viewer-Container",AutoRender:false,CSS:``};class RetoldRemoteVideoExplorerView extends libPictView{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this._currentPath='';this._frameData=null;this._selectedFrameIndex=-1;this._frameCount=20;this._fullResFrames=true;this._customFrames=[];// Selection mode and state for timeline range selection
11844
12198
  this._selectionModeActive=false;this._selectionStartTime=-1;this._selectionEndTime=-1;this._isSelectingRange=false;this._isDraggingTimeline=false;this._draggingHandle=null;// 'start', 'end', or null
11845
12199
  // Cached provider references (resolved lazily)
11846
12200
  this._fmt=null;this._provider=null;}// -----------------------------------------------------------------
@@ -11975,7 +12329,7 @@ let tmpTimeline=document.getElementById('RetoldRemote-VEX-Timeline');if(tmpTimel
11975
12329
  *
11976
12330
  * @param {string} pMessage - Error message
11977
12331
  */_showError(pMessage){let tmpBody=document.getElementById('RetoldRemote-VEX-Body');if(tmpBody){tmpBody.innerHTML='<div class="retold-remote-vex-error">'+'<div class="retold-remote-vex-error-message">'+this._getFmt().escapeHTML(pMessage||'An error occurred.')+'</div>'+'<button class="retold-remote-vex-nav-btn" onclick="pict.views[\'RetoldRemote-VideoExplorer\'].goBack()">Back to Video</button>'+'</div>';}}}// -- Mix in method groups from sub-modules --------------------------------
11978
- Object.assign(RetoldRemoteVideoExplorerView.prototype,_VideoExplorerSelection);Object.assign(RetoldRemoteVideoExplorerView.prototype,_VideoExplorerCustomFrames);Object.assign(RetoldRemoteVideoExplorerView.prototype,_VideoExplorerPreview);RetoldRemoteVideoExplorerView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteVideoExplorerView;},{"./VideoExplorer-CustomFrames":151,"./VideoExplorer-Preview":152,"./VideoExplorer-Selection":153,"pict-view":88}],151:[function(require,module,exports){/**
12332
+ Object.assign(RetoldRemoteVideoExplorerView.prototype,_VideoExplorerSelection);Object.assign(RetoldRemoteVideoExplorerView.prototype,_VideoExplorerCustomFrames);Object.assign(RetoldRemoteVideoExplorerView.prototype,_VideoExplorerPreview);RetoldRemoteVideoExplorerView.default_configuration=_ViewConfiguration;module.exports=RetoldRemoteVideoExplorerView;},{"./VideoExplorer-CustomFrames":153,"./VideoExplorer-Preview":154,"./VideoExplorer-Selection":155,"pict-view":88}],153:[function(require,module,exports){/**
11979
12333
  * VideoExplorer — Custom Frames Mixin
11980
12334
  *
11981
12335
  * Methods for extracting individual frames at arbitrary timestamps,
@@ -12057,7 +12411,7 @@ tmpEl.scrollIntoView({behavior:'smooth',block:'nearest'});},/**
12057
12411
  *
12058
12412
  * @param {string} pText - Formatted timestamp like "1:23" or "1:02:34"
12059
12413
  * @returns {number} Seconds
12060
- */_parseTimestamp:function _parseTimestamp(pText){if(!pText)return 0;let tmpParts=pText.trim().split(':');if(tmpParts.length===3){return parseInt(tmpParts[0],10)*3600+parseInt(tmpParts[1],10)*60+parseInt(tmpParts[2],10);}if(tmpParts.length===2){return parseInt(tmpParts[0],10)*60+parseInt(tmpParts[1],10);}return parseFloat(pText)||0;}};},{}],152:[function(require,module,exports){/**
12414
+ */_parseTimestamp:function _parseTimestamp(pText){if(!pText)return 0;let tmpParts=pText.trim().split(':');if(tmpParts.length===3){return parseInt(tmpParts[0],10)*3600+parseInt(tmpParts[1],10)*60+parseInt(tmpParts[2],10);}if(tmpParts.length===2){return parseInt(tmpParts[0],10)*60+parseInt(tmpParts[1],10);}return parseFloat(pText)||0;}};},{}],154:[function(require,module,exports){/**
12061
12415
  * VideoExplorer — Frame Preview Mixin
12062
12416
  *
12063
12417
  * Full-screen frame preview overlay with keyboard navigation
@@ -12110,7 +12464,7 @@ this._previewKeyHandler=e=>{switch(e.key){case'Escape':e.preventDefault();e.stop
12110
12464
  * Update the preview to show the frame at the current position.
12111
12465
  */_updatePreviewFrame:function _updatePreviewFrame(){let tmpFrame=this._previewAllFrames[this._previewPosition];if(!tmpFrame||!this._frameData){return;}// For custom frames, use the frame's own CacheKey (may differ from current batch)
12112
12466
  let tmpCacheKey=this._frameData.CacheKey;if(tmpFrame.Type==='custom'&&tmpFrame.CacheKey){tmpCacheKey=tmpFrame.CacheKey;}let tmpURL=this._buildFrameURL(tmpCacheKey,tmpFrame.Filename);let tmpBody=document.getElementById('RetoldRemote-VEX-PreviewBody');if(tmpBody){tmpBody.innerHTML='<img src="'+tmpURL+'" alt="'+this._getFmt().escapeHTML(tmpFrame.Label)+'">';}let tmpTitle=document.getElementById('RetoldRemote-VEX-PreviewTitle');if(tmpTitle){tmpTitle.textContent=tmpFrame.Label;}// Also select the corresponding frame in the grid behind the overlay
12113
- this._previewType=tmpFrame.Type;this._previewIndex=tmpFrame.Index;if(tmpFrame.Type==='regular'){this.selectFrame(tmpFrame.Index);}}};},{}],153:[function(require,module,exports){/**
12467
+ this._previewType=tmpFrame.Type;this._previewIndex=tmpFrame.Index;if(tmpFrame.Type==='regular'){this.selectFrame(tmpFrame.Index);}}};},{}],155:[function(require,module,exports){/**
12114
12468
  * VideoExplorer — Selection Mixin
12115
12469
  *
12116
12470
  * Methods for timeline range selection: toggling selection mode,