retold-remote 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/css/retold-remote.css +3 -0
  2. package/docs/README.md +181 -0
  3. package/docs/_cover.md +14 -0
  4. package/docs/_sidebar.md +10 -0
  5. package/docs/_topbar.md +3 -0
  6. package/docs/audio-viewer.md +133 -0
  7. package/docs/ebook-reader.md +90 -0
  8. package/docs/image-viewer.md +90 -0
  9. package/docs/server-setup.md +262 -0
  10. package/docs/video-viewer.md +134 -0
  11. package/html/docs.html +59 -0
  12. package/html/index.html +1 -1
  13. package/package.json +20 -6
  14. package/source/Pict-Application-RetoldRemote.js +122 -2
  15. package/source/RetoldRemote-ExtensionMaps.js +33 -0
  16. package/source/cli/RetoldRemote-Server-Setup.js +211 -67
  17. package/source/cli/commands/RetoldRemote-Command-Serve.js +5 -26
  18. package/source/providers/Pict-Provider-AISortManager.js +456 -0
  19. package/source/providers/Pict-Provider-CollectionManager.js +1200 -0
  20. package/source/providers/Pict-Provider-FormattingUtilities.js +109 -0
  21. package/source/providers/Pict-Provider-GalleryFilterSort.js +2 -11
  22. package/source/providers/Pict-Provider-GalleryNavigation.js +187 -409
  23. package/source/providers/Pict-Provider-RetoldRemoteIcons.js +52 -0
  24. package/source/providers/Pict-Provider-ToastNotification.js +96 -0
  25. package/source/providers/keyboard-handlers/KeyHandler-AudioExplorer.js +88 -0
  26. package/source/providers/keyboard-handlers/KeyHandler-Gallery.js +190 -0
  27. package/source/providers/keyboard-handlers/KeyHandler-Sidebar.js +65 -0
  28. package/source/providers/keyboard-handlers/KeyHandler-VideoExplorer.js +57 -0
  29. package/source/providers/keyboard-handlers/KeyHandler-Viewer.js +197 -0
  30. package/source/server/RetoldRemote-AISortService.js +879 -0
  31. package/source/server/RetoldRemote-ArchiveService.js +2 -12
  32. package/source/server/RetoldRemote-AudioWaveformService.js +7 -16
  33. package/source/server/RetoldRemote-CollectionService.js +843 -0
  34. package/source/server/RetoldRemote-EbookService.js +7 -16
  35. package/source/server/RetoldRemote-FileOperationService.js +560 -0
  36. package/source/server/RetoldRemote-MediaService.js +15 -14
  37. package/source/server/RetoldRemote-MetadataCache.js +411 -0
  38. package/source/server/RetoldRemote-ParimeCache.js +349 -0
  39. package/source/server/RetoldRemote-ThumbnailCache.js +52 -20
  40. package/source/server/RetoldRemote-VideoFrameService.js +7 -15
  41. package/source/views/PictView-Remote-AudioExplorer.js +10 -43
  42. package/source/views/PictView-Remote-CollectionsPanel.js +1486 -0
  43. package/source/views/PictView-Remote-Gallery.js +237 -44
  44. package/source/views/PictView-Remote-ImageViewer.js +1 -34
  45. package/source/views/PictView-Remote-Layout.js +411 -20
  46. package/source/views/PictView-Remote-MediaViewer.js +311 -28
  47. package/source/views/PictView-Remote-SettingsPanel.js +275 -133
  48. package/source/views/PictView-Remote-TopBar.js +701 -14
  49. package/source/views/PictView-Remote-VLCSetup.js +8 -39
  50. package/source/views/PictView-Remote-VideoExplorer.js +20 -54
  51. package/web-application/css/docuserve.css +73 -0
  52. package/web-application/css/retold-remote.css +3 -0
  53. package/web-application/docs/README.md +181 -0
  54. package/web-application/docs/_cover.md +14 -0
  55. package/web-application/docs/_sidebar.md +10 -0
  56. package/web-application/docs/_topbar.md +3 -0
  57. package/web-application/docs/audio-viewer.md +133 -0
  58. package/web-application/docs/ebook-reader.md +90 -0
  59. package/web-application/docs/image-viewer.md +90 -0
  60. package/web-application/docs/server-setup.md +262 -0
  61. package/web-application/docs/video-viewer.md +134 -0
  62. package/web-application/docs.html +59 -0
  63. package/web-application/index.html +1 -1
  64. package/web-application/js/pict-docuserve.min.js +58 -0
  65. package/web-application/js/pict.min.js +2 -2
  66. package/web-application/js/pict.min.js.map +1 -1
  67. package/web-application/retold-remote.js +2652 -970
  68. package/web-application/retold-remote.js.map +1 -1
  69. package/web-application/retold-remote.min.js +60 -13
  70. package/web-application/retold-remote.min.js.map +1 -1
  71. package/server.js +0 -43
@@ -97,43 +97,41 @@ const _ViewConfiguration =
97
97
  background: var(--retold-bg-hover);
98
98
  color: var(--retold-text-primary);
99
99
  }
100
- .retold-remote-settings-shortcut-group
100
+ .retold-remote-settings-input
101
101
  {
102
- margin-bottom: 10px;
102
+ width: 100%;
103
+ padding: 5px 8px;
104
+ border: 1px solid var(--retold-border);
105
+ border-radius: 3px;
106
+ background: var(--retold-bg-tertiary);
107
+ color: var(--retold-text-secondary);
108
+ font-size: 0.75rem;
109
+ font-family: inherit;
110
+ box-sizing: border-box;
103
111
  }
104
- .retold-remote-settings-shortcut-group-title
112
+ .retold-remote-settings-input:focus
105
113
  {
106
- font-size: 0.68rem;
107
- font-weight: 600;
108
- color: var(--retold-text-muted);
109
- margin-bottom: 4px;
110
- padding-bottom: 2px;
111
- border-bottom: 1px solid var(--retold-border);
114
+ outline: none;
115
+ border-color: var(--retold-accent);
112
116
  }
113
- .retold-remote-settings-shortcut-row
117
+ .retold-remote-settings-input-row
114
118
  {
115
- display: flex;
116
- justify-content: space-between;
117
- align-items: center;
118
- padding: 2px 0;
119
+ margin-bottom: 8px;
119
120
  }
120
- .retold-remote-settings-shortcut-desc
121
+ .retold-remote-settings-input-label
121
122
  {
122
- color: var(--retold-text-dim);
123
+ display: block;
123
124
  font-size: 0.72rem;
125
+ color: var(--retold-text-dim);
126
+ margin-bottom: 3px;
124
127
  }
125
- .retold-remote-settings-shortcut-key
128
+ .retold-remote-settings-template-preview
126
129
  {
127
- display: inline-block;
128
- padding: 1px 6px;
129
- border: 1px solid var(--retold-border);
130
- border-radius: 3px;
131
- background: var(--retold-bg-primary);
132
- color: var(--retold-accent);
133
130
  font-size: 0.68rem;
134
- font-family: var(--retold-font-mono, monospace);
135
- min-width: 18px;
136
- text-align: center;
131
+ color: var(--retold-text-dim);
132
+ margin-top: 3px;
133
+ font-style: italic;
134
+ word-break: break-all;
137
135
  }
138
136
  `
139
137
  };
@@ -209,6 +207,15 @@ class RetoldRemoteSettingsPanelView extends libPictView
209
207
  tmpHTML += '<div class="retold-remote-settings-section">';
210
208
  tmpHTML += '<div class="retold-remote-settings-section-title">Gallery</div>';
211
209
 
210
+ // View mode
211
+ tmpHTML += '<div class="retold-remote-settings-row">';
212
+ tmpHTML += '<span class="retold-remote-settings-label">View mode</span>';
213
+ tmpHTML += '<select class="retold-remote-settings-select" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeSetting(\'ViewMode\', this.value)">';
214
+ tmpHTML += '<option value="gallery"' + (tmpRemote.ViewMode === 'gallery' ? ' selected' : '') + '>Grid</option>';
215
+ tmpHTML += '<option value="list"' + (tmpRemote.ViewMode === 'list' ? ' selected' : '') + '>List</option>';
216
+ tmpHTML += '</select>';
217
+ tmpHTML += '</div>';
218
+
212
219
  // Thumbnail size
213
220
  tmpHTML += '<div class="retold-remote-settings-row">';
214
221
  tmpHTML += '<span class="retold-remote-settings-label">Thumbnail size</span>';
@@ -219,12 +226,36 @@ class RetoldRemoteSettingsPanelView extends libPictView
219
226
  tmpHTML += '</select>';
220
227
  tmpHTML += '</div>';
221
228
 
222
- // Default view mode
229
+ // Sort field
223
230
  tmpHTML += '<div class="retold-remote-settings-row">';
224
- tmpHTML += '<span class="retold-remote-settings-label">Default view</span>';
225
- tmpHTML += '<select class="retold-remote-settings-select" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeSetting(\'ViewMode\', this.value)">';
226
- tmpHTML += '<option value="gallery"' + (tmpRemote.ViewMode === 'gallery' ? ' selected' : '') + '>Gallery</option>';
227
- tmpHTML += '<option value="list"' + (tmpRemote.ViewMode === 'list' ? ' selected' : '') + '>List</option>';
231
+ tmpHTML += '<span class="retold-remote-settings-label">Sort by</span>';
232
+ tmpHTML += '<select class="retold-remote-settings-select" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeSortField(this.value)">';
233
+ tmpHTML += '<option value="folder-first"' + (tmpRemote.SortField === 'folder-first' ? ' selected' : '') + '>Folders first</option>';
234
+ tmpHTML += '<option value="name"' + (tmpRemote.SortField === 'name' ? ' selected' : '') + '>Name</option>';
235
+ tmpHTML += '<option value="modified"' + (tmpRemote.SortField === 'modified' ? ' selected' : '') + '>Modified</option>';
236
+ tmpHTML += '<option value="created"' + (tmpRemote.SortField === 'created' ? ' selected' : '') + '>Created</option>';
237
+ tmpHTML += '</select>';
238
+ tmpHTML += '</div>';
239
+
240
+ // Sort direction
241
+ tmpHTML += '<div class="retold-remote-settings-row">';
242
+ tmpHTML += '<span class="retold-remote-settings-label">Sort direction</span>';
243
+ tmpHTML += '<select class="retold-remote-settings-select" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeSortDirection(this.value)">';
244
+ tmpHTML += '<option value="asc"' + (tmpRemote.SortDirection === 'asc' ? ' selected' : '') + '>Ascending</option>';
245
+ tmpHTML += '<option value="desc"' + (tmpRemote.SortDirection === 'desc' ? ' selected' : '') + '>Descending</option>';
246
+ tmpHTML += '</select>';
247
+ tmpHTML += '</div>';
248
+
249
+ // Default media filter
250
+ let tmpFilterMediaType = (tmpRemote.FilterState && tmpRemote.FilterState.MediaType) || 'all';
251
+ tmpHTML += '<div class="retold-remote-settings-row">';
252
+ tmpHTML += '<span class="retold-remote-settings-label">Media filter</span>';
253
+ tmpHTML += '<select class="retold-remote-settings-select" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeMediaFilter(this.value)">';
254
+ tmpHTML += '<option value="all"' + (tmpFilterMediaType === 'all' ? ' selected' : '') + '>All files</option>';
255
+ tmpHTML += '<option value="images"' + (tmpFilterMediaType === 'images' ? ' selected' : '') + '>Images</option>';
256
+ tmpHTML += '<option value="video"' + (tmpFilterMediaType === 'video' ? ' selected' : '') + '>Video</option>';
257
+ tmpHTML += '<option value="audio"' + (tmpFilterMediaType === 'audio' ? ' selected' : '') + '>Audio</option>';
258
+ tmpHTML += '<option value="documents"' + (tmpFilterMediaType === 'documents' ? ' selected' : '') + '>Documents</option>';
228
259
  tmpHTML += '</select>';
229
260
  tmpHTML += '</div>';
230
261
 
@@ -236,12 +267,20 @@ class RetoldRemoteSettingsPanelView extends libPictView
236
267
  + ' onchange="pict.views[\'RetoldRemote-SettingsPanel\'].toggleHiddenFiles(this.checked)">';
237
268
  tmpHTML += '</div>';
238
269
 
239
- // Navigation bar in distraction-free
270
+ tmpHTML += '</div>'; // end gallery section
271
+
272
+ // Viewer section
273
+ tmpHTML += '<div class="retold-remote-settings-section">';
274
+ tmpHTML += '<div class="retold-remote-settings-section-title">Viewer</div>';
275
+
276
+ // Image fit mode
240
277
  tmpHTML += '<div class="retold-remote-settings-row">';
241
- tmpHTML += '<span class="retold-remote-settings-label">Nav bar in distraction-free</span>';
242
- tmpHTML += '<input type="checkbox" class="retold-remote-settings-checkbox"'
243
- + (tmpRemote.DistractionFreeShowNav ? ' checked' : '')
244
- + ' onchange="pict.views[\'RetoldRemote-SettingsPanel\'].toggleDistractionFreeNav(this.checked)">';
278
+ tmpHTML += '<span class="retold-remote-settings-label">Image fit mode</span>';
279
+ tmpHTML += '<select class="retold-remote-settings-select" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeImageFitMode(this.value)">';
280
+ tmpHTML += '<option value="fit"' + (tmpRemote.ImageFitMode === 'fit' ? ' selected' : '') + '>Fit to window</option>';
281
+ tmpHTML += '<option value="auto"' + (tmpRemote.ImageFitMode === 'auto' ? ' selected' : '') + '>Original if smaller</option>';
282
+ tmpHTML += '<option value="original"' + (tmpRemote.ImageFitMode === 'original' ? ' selected' : '') + '>Original size</option>';
283
+ tmpHTML += '</select>';
245
284
  tmpHTML += '</div>';
246
285
 
247
286
  // Autoplay video
@@ -260,7 +299,15 @@ class RetoldRemoteSettingsPanelView extends libPictView
260
299
  + ' onchange="pict.views[\'RetoldRemote-SettingsPanel\'].toggleAutoplay(\'AutoplayAudio\', this.checked)">';
261
300
  tmpHTML += '</div>';
262
301
 
263
- tmpHTML += '</div>'; // end gallery section
302
+ // Navigation bar in distraction-free
303
+ tmpHTML += '<div class="retold-remote-settings-row">';
304
+ tmpHTML += '<span class="retold-remote-settings-label">Nav bar in distraction-free</span>';
305
+ tmpHTML += '<input type="checkbox" class="retold-remote-settings-checkbox"'
306
+ + (tmpRemote.DistractionFreeShowNav ? ' checked' : '')
307
+ + ' onchange="pict.views[\'RetoldRemote-SettingsPanel\'].toggleDistractionFreeNav(this.checked)">';
308
+ tmpHTML += '</div>';
309
+
310
+ tmpHTML += '</div>'; // end viewer section
264
311
 
265
312
  // Server capabilities
266
313
  tmpHTML += '<div class="retold-remote-settings-section">';
@@ -295,6 +342,60 @@ class RetoldRemoteSettingsPanelView extends libPictView
295
342
  tmpHTML += '</div>';
296
343
  tmpHTML += '</div>'; // end capabilities section
297
344
 
345
+ // AI File Sort
346
+ tmpHTML += '<div class="retold-remote-settings-section">';
347
+ tmpHTML += '<div class="retold-remote-settings-section-title">AI File Sort</div>';
348
+
349
+ let tmpAISortManager = this.pict.providers['RetoldRemote-AISortManager'];
350
+ let tmpAISettings = tmpRemote.AISortSettings ||
351
+ {
352
+ AIEndpoint: 'http://localhost:11434',
353
+ AIModel: 'llama3.1',
354
+ AIProvider: 'ollama',
355
+ NamingTemplate: '{artist}/{album}/{track} - {title}'
356
+ };
357
+
358
+ // AI Endpoint
359
+ tmpHTML += '<div class="retold-remote-settings-input-row">';
360
+ tmpHTML += '<label class="retold-remote-settings-input-label">AI Endpoint URL</label>';
361
+ tmpHTML += '<input class="retold-remote-settings-input" type="text" id="RetoldRemote-AISortEndpoint" value="' + this._escapeAttr(tmpAISettings.AIEndpoint) + '" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeAISetting(\'AIEndpoint\', this.value)" placeholder="http://localhost:11434">';
362
+ tmpHTML += '</div>';
363
+
364
+ // AI Model
365
+ tmpHTML += '<div class="retold-remote-settings-input-row">';
366
+ tmpHTML += '<label class="retold-remote-settings-input-label">Model</label>';
367
+ tmpHTML += '<input class="retold-remote-settings-input" type="text" id="RetoldRemote-AISortModel" value="' + this._escapeAttr(tmpAISettings.AIModel) + '" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeAISetting(\'AIModel\', this.value)" placeholder="llama3.1">';
368
+ tmpHTML += '</div>';
369
+
370
+ // AI Provider
371
+ tmpHTML += '<div class="retold-remote-settings-row">';
372
+ tmpHTML += '<span class="retold-remote-settings-label">Provider</span>';
373
+ tmpHTML += '<select class="retold-remote-settings-select" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeAISetting(\'AIProvider\', this.value)">';
374
+ tmpHTML += '<option value="ollama"' + (tmpAISettings.AIProvider === 'ollama' ? ' selected' : '') + '>Ollama</option>';
375
+ tmpHTML += '<option value="openai"' + (tmpAISettings.AIProvider === 'openai' ? ' selected' : '') + '>OpenAI-compatible</option>';
376
+ tmpHTML += '</select>';
377
+ tmpHTML += '</div>';
378
+
379
+ // Naming Template
380
+ tmpHTML += '<div class="retold-remote-settings-input-row" style="margin-top: 8px;">';
381
+ tmpHTML += '<label class="retold-remote-settings-input-label">Naming Template</label>';
382
+ tmpHTML += '<input class="retold-remote-settings-input" type="text" id="RetoldRemote-AISortTemplate" value="' + this._escapeAttr(tmpAISettings.NamingTemplate) + '" onchange="pict.views[\'RetoldRemote-SettingsPanel\'].changeAISetting(\'NamingTemplate\', this.value)" placeholder="{artist}/{album}/{track} - {title}">';
383
+
384
+ // Template preview
385
+ let tmpTemplatePreview = tmpAISortManager ? tmpAISortManager.getTemplatePreview(tmpAISettings.NamingTemplate) : '';
386
+ if (tmpTemplatePreview)
387
+ {
388
+ tmpHTML += '<div class="retold-remote-settings-template-preview">Preview: ' + this._escapeHTML(tmpTemplatePreview) + '</div>';
389
+ }
390
+ tmpHTML += '</div>';
391
+
392
+ // Test Connection button
393
+ tmpHTML += '<button class="retold-remote-settings-vlc-btn" id="RetoldRemote-AISortTestBtn" onclick="pict.views[\'RetoldRemote-SettingsPanel\'].testAIConnection()" style="margin-top: 8px;">';
394
+ tmpHTML += 'Test Connection';
395
+ tmpHTML += '</button>';
396
+
397
+ tmpHTML += '</div>'; // end AI sort section
398
+
298
399
  // VLC Setup
299
400
  tmpHTML += '<div class="retold-remote-settings-section">';
300
401
  tmpHTML += '<div class="retold-remote-settings-section-title">VLC Streaming</div>';
@@ -303,109 +404,19 @@ class RetoldRemoteSettingsPanelView extends libPictView
303
404
  tmpHTML += '</button>';
304
405
  tmpHTML += '</div>';
305
406
 
306
- // Keyboard shortcuts
407
+ // Help button
307
408
  tmpHTML += '<div class="retold-remote-settings-section">';
308
- tmpHTML += '<div class="retold-remote-settings-section-title">Keyboard Shortcuts</div>';
309
-
310
- tmpHTML += this._buildShortcutGroup('Global',
311
- [
312
- { key: 'F1', desc: 'Help panel' },
313
- { key: 'F9', desc: 'Focus sidebar' },
314
- { key: '/', desc: 'Search / filter bar' },
315
- { key: 'Esc', desc: 'Close overlay / back' }
316
- ]);
317
-
318
- tmpHTML += this._buildShortcutGroup('Gallery',
319
- [
320
- { key: '\u2190 \u2191 \u2192 \u2193', desc: 'Navigate items' },
321
- { key: 'Enter', desc: 'Open item' },
322
- { key: '1 2 3 4', desc: 'Open as image / video / audio / text' },
323
- { key: 'Esc', desc: 'Go up one folder' },
324
- { key: 'Home', desc: 'Jump to first item' },
325
- { key: 'End', desc: 'Jump to last item' },
326
- { key: 'g', desc: 'Toggle grid / list' },
327
- { key: 'f', desc: 'Advanced filter panel' },
328
- { key: 's', desc: 'Focus sort dropdown' },
329
- { key: 'x', desc: 'Clear all filters' },
330
- { key: 'c', desc: 'Settings panel' },
331
- { key: 'd', desc: 'Distraction-free mode' }
332
- ]);
333
-
334
- tmpHTML += this._buildShortcutGroup('Sidebar (F9)',
335
- [
336
- { key: '\u2191 \u2193', desc: 'Navigate file list' },
337
- { key: 'Home', desc: 'Jump to first' },
338
- { key: 'End', desc: 'Jump to last' },
339
- { key: 'Enter', desc: 'Open item' },
340
- { key: 'Esc', desc: 'Return to gallery' }
341
- ]);
342
-
343
- tmpHTML += this._buildShortcutGroup('Media Viewer',
344
- [
345
- { key: 'Esc', desc: 'Back to gallery' },
346
- { key: '\u2192 j', desc: 'Next file' },
347
- { key: '\u2190 k', desc: 'Previous file' },
348
- { key: '1 2 3 4', desc: 'View as image / video / audio / text' },
349
- { key: 'Space', desc: 'Play / pause' },
350
- { key: 'f', desc: 'Fullscreen' },
351
- { key: 'i', desc: 'File info overlay' },
352
- { key: 'v', desc: 'Stream with VLC' },
353
- { key: '+ -', desc: 'Zoom in / out' },
354
- { key: '0', desc: 'Reset zoom' },
355
- { key: 'z', desc: 'Cycle fit mode' },
356
- { key: 'd', desc: 'Distraction-free mode' }
357
- ]);
358
-
359
- tmpHTML += this._buildShortcutGroup('Video Menu',
360
- [
361
- { key: 'Space', desc: 'Play in browser' },
362
- { key: 'Enter', desc: 'Play in browser' },
363
- { key: 'e', desc: 'Explore video frames' },
364
- { key: 't', desc: 'Extract thumbnail' },
365
- { key: 'v', desc: 'Stream with VLC' },
366
- { key: '\u2192 j', desc: 'Next file' },
367
- { key: '\u2190 k', desc: 'Previous file' },
368
- { key: 'Esc', desc: 'Back to gallery' }
369
- ]);
370
-
371
- tmpHTML += this._buildShortcutGroup('Video Explorer',
372
- [
373
- { key: 'Esc', desc: 'Back' }
374
- ]);
375
-
376
- tmpHTML += this._buildShortcutGroup('Audio Explorer',
377
- [
378
- { key: 'Space', desc: 'Play selection' },
379
- { key: '+ -', desc: 'Zoom in / out' },
380
- { key: '0', desc: 'Zoom to fit' },
381
- { key: 'z', desc: 'Zoom to selection' },
382
- { key: 'Esc', desc: 'Clear selection / back' }
383
- ]);
384
-
385
- tmpHTML += '</div>'; // end shortcuts section
409
+ tmpHTML += '<div class="retold-remote-settings-section-title">Help</div>';
410
+ tmpHTML += '<button class="retold-remote-settings-vlc-btn" onclick="pict.providers[\'RetoldRemote-GalleryNavigation\']._toggleHelpPanel()">';
411
+ tmpHTML += 'Help (F1)';
412
+ tmpHTML += '</button>';
413
+ tmpHTML += '</div>';
386
414
 
387
415
  tmpHTML += '</div>'; // end settings
388
416
 
389
417
  tmpContainer.innerHTML = tmpHTML;
390
418
  }
391
419
 
392
- _buildShortcutGroup(pTitle, pShortcuts)
393
- {
394
- let tmpHTML = '<div class="retold-remote-settings-shortcut-group">';
395
- tmpHTML += '<div class="retold-remote-settings-shortcut-group-title">' + pTitle + '</div>';
396
-
397
- for (let i = 0; i < pShortcuts.length; i++)
398
- {
399
- tmpHTML += '<div class="retold-remote-settings-shortcut-row">';
400
- tmpHTML += '<span class="retold-remote-settings-shortcut-desc">' + pShortcuts[i].desc + '</span>';
401
- tmpHTML += '<span class="retold-remote-settings-shortcut-key">' + pShortcuts[i].key + '</span>';
402
- tmpHTML += '</div>';
403
- }
404
-
405
- tmpHTML += '</div>';
406
- return tmpHTML;
407
- }
408
-
409
420
  changeTheme(pThemeKey)
410
421
  {
411
422
  let tmpThemeProvider = this.pict.providers['RetoldRemote-Theme'];
@@ -470,6 +481,137 @@ class RetoldRemoteSettingsPanelView extends libPictView
470
481
  }
471
482
  }
472
483
  }
484
+
485
+ changeImageFitMode(pMode)
486
+ {
487
+ let tmpImageViewer = this.pict.views['RetoldRemote-ImageViewer'];
488
+ if (tmpImageViewer)
489
+ {
490
+ tmpImageViewer.setFitMode(pMode);
491
+ }
492
+ else
493
+ {
494
+ let tmpRemote = this.pict.AppData.RetoldRemote;
495
+ tmpRemote.ImageFitMode = pMode;
496
+ this.pict.PictApplication.saveSettings();
497
+ }
498
+ }
499
+
500
+ changeSortField(pValue)
501
+ {
502
+ let tmpRemote = this.pict.AppData.RetoldRemote;
503
+ tmpRemote.SortField = pValue;
504
+ this.pict.PictApplication.saveSettings();
505
+ this._refilterGallery();
506
+ }
507
+
508
+ changeSortDirection(pValue)
509
+ {
510
+ let tmpRemote = this.pict.AppData.RetoldRemote;
511
+ tmpRemote.SortDirection = pValue;
512
+ this.pict.PictApplication.saveSettings();
513
+ this._refilterGallery();
514
+ }
515
+
516
+ changeMediaFilter(pValue)
517
+ {
518
+ let tmpRemote = this.pict.AppData.RetoldRemote;
519
+ tmpRemote.GalleryFilter = pValue;
520
+ if (tmpRemote.FilterState)
521
+ {
522
+ tmpRemote.FilterState.MediaType = pValue;
523
+ }
524
+ this.pict.PictApplication.saveSettings();
525
+ this._refilterGallery();
526
+ }
527
+
528
+ /**
529
+ * Change an AI sort setting.
530
+ *
531
+ * @param {string} pKey - Setting key (AIEndpoint, AIModel, AIProvider, NamingTemplate)
532
+ * @param {string} pValue - New value
533
+ */
534
+ changeAISetting(pKey, pValue)
535
+ {
536
+ let tmpAISortManager = this.pict.providers['RetoldRemote-AISortManager'];
537
+ if (tmpAISortManager)
538
+ {
539
+ let tmpUpdate = {};
540
+ tmpUpdate[pKey] = pValue;
541
+ tmpAISortManager.updateSettings(tmpUpdate);
542
+ }
543
+
544
+ // Update the template preview if the template changed
545
+ if (pKey === 'NamingTemplate')
546
+ {
547
+ this._renderSettingsContent();
548
+ }
549
+ }
550
+
551
+ /**
552
+ * Test the AI endpoint connection.
553
+ */
554
+ testAIConnection()
555
+ {
556
+ let tmpBtn = document.getElementById('RetoldRemote-AISortTestBtn');
557
+ if (tmpBtn)
558
+ {
559
+ tmpBtn.disabled = true;
560
+ tmpBtn.textContent = 'Testing...';
561
+ }
562
+
563
+ let tmpAISortManager = this.pict.providers['RetoldRemote-AISortManager'];
564
+ if (tmpAISortManager)
565
+ {
566
+ tmpAISortManager.testConnection();
567
+ }
568
+ }
569
+
570
+ /**
571
+ * Escape HTML attribute values.
572
+ *
573
+ * @param {string} pStr
574
+ * @returns {string}
575
+ */
576
+ _escapeAttr(pStr)
577
+ {
578
+ if (!pStr) return '';
579
+ return String(pStr).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
580
+ }
581
+
582
+ /**
583
+ * Escape HTML text content.
584
+ *
585
+ * @param {string} pStr
586
+ * @returns {string}
587
+ */
588
+ _escapeHTML(pStr)
589
+ {
590
+ if (!pStr) return '';
591
+ return String(pStr).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
592
+ }
593
+
594
+ /**
595
+ * Re-run the filter/sort pipeline and refresh the gallery.
596
+ */
597
+ _refilterGallery()
598
+ {
599
+ let tmpRemote = this.pict.AppData.RetoldRemote;
600
+ let tmpFilterSort = this.pict.providers['RetoldRemote-GalleryFilterSort'];
601
+ if (tmpFilterSort)
602
+ {
603
+ tmpFilterSort.runFilterPipeline();
604
+ }
605
+
606
+ if (tmpRemote.ActiveMode === 'gallery')
607
+ {
608
+ let tmpGalleryView = this.pict.views['RetoldRemote-Gallery'];
609
+ if (tmpGalleryView)
610
+ {
611
+ tmpGalleryView.renderGallery();
612
+ }
613
+ }
614
+ }
473
615
  }
474
616
 
475
617
  RetoldRemoteSettingsPanelView.default_configuration = _ViewConfiguration;