retold-remote 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/README.md +181 -0
- package/docs/_cover.md +14 -0
- package/docs/_sidebar.md +10 -0
- package/docs/_topbar.md +3 -0
- package/docs/audio-viewer.md +133 -0
- package/docs/ebook-reader.md +90 -0
- package/docs/image-viewer.md +90 -0
- package/docs/server-setup.md +262 -0
- package/docs/video-viewer.md +134 -0
- package/html/docs.html +59 -0
- package/package.json +21 -7
- package/source/Pict-Application-RetoldRemote.js +143 -2
- package/source/RetoldRemote-ExtensionMaps.js +33 -0
- package/source/cli/RetoldRemote-Server-Setup.js +82 -67
- package/source/cli/commands/RetoldRemote-Command-Serve.js +5 -26
- package/source/providers/Pict-Provider-CollectionManager.js +934 -0
- package/source/providers/Pict-Provider-FormattingUtilities.js +109 -0
- package/source/providers/Pict-Provider-GalleryFilterSort.js +2 -11
- package/source/providers/Pict-Provider-GalleryNavigation.js +270 -353
- package/source/providers/Pict-Provider-RetoldRemoteIcons.js +52 -0
- package/source/providers/Pict-Provider-ToastNotification.js +96 -0
- package/source/providers/keyboard-handlers/KeyHandler-AudioExplorer.js +88 -0
- package/source/providers/keyboard-handlers/KeyHandler-Gallery.js +190 -0
- package/source/providers/keyboard-handlers/KeyHandler-Sidebar.js +65 -0
- package/source/providers/keyboard-handlers/KeyHandler-VideoExplorer.js +57 -0
- package/source/providers/keyboard-handlers/KeyHandler-Viewer.js +197 -0
- package/source/server/RetoldRemote-ArchiveService.js +2 -12
- package/source/server/RetoldRemote-AudioWaveformService.js +7 -16
- package/source/server/RetoldRemote-CollectionService.js +684 -0
- package/source/server/RetoldRemote-EbookService.js +7 -16
- package/source/server/RetoldRemote-MediaService.js +3 -14
- package/source/server/RetoldRemote-ParimeCache.js +349 -0
- package/source/server/RetoldRemote-ThumbnailCache.js +52 -20
- package/source/server/RetoldRemote-VideoFrameService.js +7 -15
- package/source/views/PictView-Remote-AudioExplorer.js +10 -43
- package/source/views/PictView-Remote-CollectionsPanel.js +1087 -0
- package/source/views/PictView-Remote-Gallery.js +237 -44
- package/source/views/PictView-Remote-ImageViewer.js +1 -34
- package/source/views/PictView-Remote-Layout.js +410 -20
- package/source/views/PictView-Remote-MediaViewer.js +338 -51
- package/source/views/PictView-Remote-SettingsPanel.js +155 -138
- package/source/views/PictView-Remote-TopBar.js +615 -14
- package/source/views/PictView-Remote-VLCSetup.js +766 -0
- package/source/views/PictView-Remote-VideoExplorer.js +20 -54
- package/web-application/css/docuserve.css +73 -0
- package/web-application/docs/README.md +181 -0
- package/web-application/docs/_cover.md +14 -0
- package/web-application/docs/_sidebar.md +10 -0
- package/web-application/docs/_topbar.md +3 -0
- package/web-application/docs/audio-viewer.md +133 -0
- package/web-application/docs/ebook-reader.md +90 -0
- package/web-application/docs/image-viewer.md +90 -0
- package/web-application/docs/server-setup.md +262 -0
- package/web-application/docs/video-viewer.md +134 -0
- package/web-application/docs.html +59 -0
- package/web-application/js/pict-docuserve.min.js +58 -0
- package/web-application/js/pict.min.js +2 -2
- package/web-application/js/pict.min.js.map +1 -1
- package/web-application/retold-remote.js +2558 -439
- package/web-application/retold-remote.js.map +1 -1
- package/web-application/retold-remote.min.js +41 -11
- package/web-application/retold-remote.min.js.map +1 -1
- package/server.js +0 -43
|
@@ -20,22 +20,76 @@ const _ViewConfiguration =
|
|
|
20
20
|
border-bottom: 1px solid var(--retold-border);
|
|
21
21
|
gap: 16px;
|
|
22
22
|
}
|
|
23
|
-
.retold-remote-topbar-
|
|
23
|
+
.retold-remote-topbar-sidebar-toggle
|
|
24
|
+
{
|
|
25
|
+
flex-shrink: 0;
|
|
26
|
+
display: inline-flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
justify-content: center;
|
|
29
|
+
width: 32px;
|
|
30
|
+
height: 32px;
|
|
31
|
+
padding: 0;
|
|
32
|
+
margin: 0;
|
|
33
|
+
border: 1px solid var(--retold-border);
|
|
34
|
+
border-radius: 4px;
|
|
35
|
+
background: transparent;
|
|
36
|
+
color: var(--retold-text-muted);
|
|
37
|
+
font-size: 1rem;
|
|
38
|
+
line-height: 1;
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
|
41
|
+
font-family: inherit;
|
|
42
|
+
-webkit-tap-highlight-color: transparent;
|
|
43
|
+
}
|
|
44
|
+
.retold-remote-topbar-sidebar-toggle:hover,
|
|
45
|
+
.retold-remote-topbar-sidebar-toggle:active,
|
|
46
|
+
.retold-remote-topbar-df-toggle:hover,
|
|
47
|
+
.retold-remote-topbar-df-toggle:active
|
|
48
|
+
{
|
|
49
|
+
color: var(--retold-text-primary);
|
|
50
|
+
border-color: var(--retold-accent);
|
|
51
|
+
background: rgba(128, 128, 128, 0.1);
|
|
52
|
+
}
|
|
53
|
+
.retold-remote-topbar-df-toggle
|
|
24
54
|
{
|
|
25
|
-
font-size: 0.85rem;
|
|
26
|
-
font-weight: 700;
|
|
27
|
-
color: var(--retold-accent);
|
|
28
55
|
flex-shrink: 0;
|
|
56
|
+
display: inline-flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
width: 32px;
|
|
60
|
+
height: 32px;
|
|
61
|
+
padding: 0;
|
|
62
|
+
margin: 0;
|
|
63
|
+
border: 1px solid var(--retold-border);
|
|
64
|
+
border-radius: 4px;
|
|
65
|
+
background: transparent;
|
|
66
|
+
color: var(--retold-text-muted);
|
|
67
|
+
font-size: 1rem;
|
|
68
|
+
line-height: 1;
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
|
71
|
+
font-family: inherit;
|
|
72
|
+
-webkit-tap-highlight-color: transparent;
|
|
29
73
|
}
|
|
30
74
|
.retold-remote-topbar-location
|
|
31
75
|
{
|
|
76
|
+
position: relative;
|
|
32
77
|
flex: 1;
|
|
33
78
|
font-size: 0.82rem;
|
|
34
79
|
color: var(--retold-text-muted);
|
|
80
|
+
white-space: nowrap;
|
|
81
|
+
text-align: center;
|
|
82
|
+
display: flex;
|
|
83
|
+
align-items: center;
|
|
84
|
+
justify-content: center;
|
|
85
|
+
overflow: visible;
|
|
86
|
+
min-width: 0;
|
|
87
|
+
}
|
|
88
|
+
.retold-remote-topbar-location-inner
|
|
89
|
+
{
|
|
35
90
|
overflow: hidden;
|
|
36
91
|
text-overflow: ellipsis;
|
|
37
92
|
white-space: nowrap;
|
|
38
|
-
text-align: center;
|
|
39
93
|
}
|
|
40
94
|
.retold-remote-topbar-location-crumb
|
|
41
95
|
{
|
|
@@ -47,11 +101,120 @@ const _ViewConfiguration =
|
|
|
47
101
|
{
|
|
48
102
|
text-decoration: underline;
|
|
49
103
|
}
|
|
104
|
+
.retold-remote-topbar-home-crumb
|
|
105
|
+
{
|
|
106
|
+
cursor: pointer;
|
|
107
|
+
display: inline-flex;
|
|
108
|
+
align-items: center;
|
|
109
|
+
vertical-align: middle;
|
|
110
|
+
opacity: 0.8;
|
|
111
|
+
}
|
|
112
|
+
.retold-remote-topbar-home-crumb:hover
|
|
113
|
+
{
|
|
114
|
+
opacity: 1;
|
|
115
|
+
}
|
|
50
116
|
.retold-remote-topbar-sep
|
|
51
117
|
{
|
|
52
118
|
color: var(--retold-text-placeholder);
|
|
53
119
|
margin: 0 3px;
|
|
54
120
|
}
|
|
121
|
+
/* Breadcrumb overflow hamburger */
|
|
122
|
+
.retold-remote-topbar-breadcrumb-overflow
|
|
123
|
+
{
|
|
124
|
+
position: relative;
|
|
125
|
+
display: inline-flex;
|
|
126
|
+
align-items: center;
|
|
127
|
+
vertical-align: middle;
|
|
128
|
+
}
|
|
129
|
+
.retold-remote-topbar-overflow-btn
|
|
130
|
+
{
|
|
131
|
+
display: inline-flex;
|
|
132
|
+
align-items: center;
|
|
133
|
+
justify-content: center;
|
|
134
|
+
width: 28px;
|
|
135
|
+
height: 28px;
|
|
136
|
+
padding: 0;
|
|
137
|
+
margin: 0;
|
|
138
|
+
border: 1px solid var(--retold-border);
|
|
139
|
+
border-radius: 4px;
|
|
140
|
+
background: transparent;
|
|
141
|
+
color: var(--retold-text-muted);
|
|
142
|
+
font-size: 0.9rem;
|
|
143
|
+
line-height: 1;
|
|
144
|
+
cursor: pointer;
|
|
145
|
+
transition: color 0.15s, border-color 0.15s, background 0.15s;
|
|
146
|
+
font-family: inherit;
|
|
147
|
+
-webkit-tap-highlight-color: transparent;
|
|
148
|
+
}
|
|
149
|
+
.retold-remote-topbar-overflow-btn:hover,
|
|
150
|
+
.retold-remote-topbar-overflow-btn:active
|
|
151
|
+
{
|
|
152
|
+
color: var(--retold-text-primary);
|
|
153
|
+
border-color: var(--retold-accent);
|
|
154
|
+
background: rgba(128, 128, 128, 0.1);
|
|
155
|
+
}
|
|
156
|
+
.retold-remote-topbar-overflow-dropdown
|
|
157
|
+
{
|
|
158
|
+
display: none;
|
|
159
|
+
position: absolute;
|
|
160
|
+
top: 100%;
|
|
161
|
+
left: 0;
|
|
162
|
+
margin-top: 4px;
|
|
163
|
+
min-width: 200px;
|
|
164
|
+
max-width: 300px;
|
|
165
|
+
background: var(--retold-bg-secondary);
|
|
166
|
+
border: 1px solid var(--retold-border);
|
|
167
|
+
border-radius: 6px;
|
|
168
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
|
|
169
|
+
z-index: 1000;
|
|
170
|
+
overflow: hidden;
|
|
171
|
+
}
|
|
172
|
+
.retold-remote-topbar-overflow-dropdown.open
|
|
173
|
+
{
|
|
174
|
+
display: block;
|
|
175
|
+
}
|
|
176
|
+
.retold-remote-topbar-overflow-item
|
|
177
|
+
{
|
|
178
|
+
display: flex;
|
|
179
|
+
align-items: center;
|
|
180
|
+
gap: 8px;
|
|
181
|
+
width: 100%;
|
|
182
|
+
padding: 12px 16px;
|
|
183
|
+
border: none;
|
|
184
|
+
border-bottom: 1px solid var(--retold-border);
|
|
185
|
+
background: transparent;
|
|
186
|
+
color: var(--retold-text-primary);
|
|
187
|
+
font-size: 0.9rem;
|
|
188
|
+
text-align: left;
|
|
189
|
+
cursor: pointer;
|
|
190
|
+
font-family: inherit;
|
|
191
|
+
-webkit-tap-highlight-color: transparent;
|
|
192
|
+
min-height: 44px;
|
|
193
|
+
box-sizing: border-box;
|
|
194
|
+
}
|
|
195
|
+
.retold-remote-topbar-overflow-item:last-child
|
|
196
|
+
{
|
|
197
|
+
border-bottom: none;
|
|
198
|
+
}
|
|
199
|
+
.retold-remote-topbar-overflow-item:hover,
|
|
200
|
+
.retold-remote-topbar-overflow-item:active
|
|
201
|
+
{
|
|
202
|
+
background: rgba(128, 128, 128, 0.12);
|
|
203
|
+
color: var(--retold-accent);
|
|
204
|
+
}
|
|
205
|
+
.retold-remote-topbar-overflow-item-icon
|
|
206
|
+
{
|
|
207
|
+
display: inline-flex;
|
|
208
|
+
align-items: center;
|
|
209
|
+
flex-shrink: 0;
|
|
210
|
+
opacity: 0.7;
|
|
211
|
+
}
|
|
212
|
+
.retold-remote-topbar-overflow-item-label
|
|
213
|
+
{
|
|
214
|
+
overflow: hidden;
|
|
215
|
+
text-overflow: ellipsis;
|
|
216
|
+
white-space: nowrap;
|
|
217
|
+
}
|
|
55
218
|
.retold-remote-topbar-info
|
|
56
219
|
{
|
|
57
220
|
flex-shrink: 0;
|
|
@@ -81,6 +244,64 @@ const _ViewConfiguration =
|
|
|
81
244
|
color: var(--retold-text-primary);
|
|
82
245
|
border-color: var(--retold-accent);
|
|
83
246
|
}
|
|
247
|
+
.retold-remote-topbar-addcoll-btn
|
|
248
|
+
{
|
|
249
|
+
font-size: 0.72rem;
|
|
250
|
+
}
|
|
251
|
+
.retold-remote-topbar-collections-btn.panel-open
|
|
252
|
+
{
|
|
253
|
+
color: var(--retold-accent);
|
|
254
|
+
border-color: var(--retold-accent);
|
|
255
|
+
background: rgba(128, 128, 128, 0.1);
|
|
256
|
+
}
|
|
257
|
+
/* Add-to-collection dropdown */
|
|
258
|
+
.retold-remote-addcoll-dropdown
|
|
259
|
+
{
|
|
260
|
+
position: absolute;
|
|
261
|
+
top: 100%;
|
|
262
|
+
right: 0;
|
|
263
|
+
margin-top: 4px;
|
|
264
|
+
min-width: 220px;
|
|
265
|
+
max-width: 320px;
|
|
266
|
+
max-height: 300px;
|
|
267
|
+
overflow-y: auto;
|
|
268
|
+
background: var(--retold-bg-secondary);
|
|
269
|
+
border: 1px solid var(--retold-border);
|
|
270
|
+
border-radius: 6px;
|
|
271
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
|
|
272
|
+
z-index: 1000;
|
|
273
|
+
}
|
|
274
|
+
.retold-remote-addcoll-dropdown-item
|
|
275
|
+
{
|
|
276
|
+
display: flex;
|
|
277
|
+
align-items: center;
|
|
278
|
+
gap: 8px;
|
|
279
|
+
width: 100%;
|
|
280
|
+
padding: 10px 14px;
|
|
281
|
+
border: none;
|
|
282
|
+
border-bottom: 1px solid var(--retold-border);
|
|
283
|
+
background: transparent;
|
|
284
|
+
color: var(--retold-text-primary);
|
|
285
|
+
font-size: 0.82rem;
|
|
286
|
+
text-align: left;
|
|
287
|
+
cursor: pointer;
|
|
288
|
+
font-family: inherit;
|
|
289
|
+
box-sizing: border-box;
|
|
290
|
+
}
|
|
291
|
+
.retold-remote-addcoll-dropdown-item:last-child
|
|
292
|
+
{
|
|
293
|
+
border-bottom: none;
|
|
294
|
+
}
|
|
295
|
+
.retold-remote-addcoll-dropdown-item:hover
|
|
296
|
+
{
|
|
297
|
+
background: rgba(128, 128, 128, 0.12);
|
|
298
|
+
color: var(--retold-accent);
|
|
299
|
+
}
|
|
300
|
+
.retold-remote-addcoll-dropdown-new
|
|
301
|
+
{
|
|
302
|
+
color: var(--retold-accent);
|
|
303
|
+
font-weight: 500;
|
|
304
|
+
}
|
|
84
305
|
.retold-remote-topbar-filter-btn
|
|
85
306
|
{
|
|
86
307
|
position: relative;
|
|
@@ -136,12 +357,14 @@ const _ViewConfiguration =
|
|
|
136
357
|
Hash: "RetoldRemote-TopBar",
|
|
137
358
|
Template: /*html*/`
|
|
138
359
|
<div class="retold-remote-topbar">
|
|
139
|
-
<
|
|
360
|
+
<button class="retold-remote-topbar-sidebar-toggle" id="RetoldRemote-TopBar-SidebarToggle" onclick="pict.views['ContentEditor-Layout'].toggleSidebar()" title="Toggle Sidebar"></button>
|
|
361
|
+
<button class="retold-remote-topbar-df-toggle" id="RetoldRemote-TopBar-DFToggle" onclick="pict.views['ContentEditor-TopBar'].toggleDistractionFree()" title="Distraction-free mode (d)"></button>
|
|
140
362
|
<div class="retold-remote-topbar-location" id="RetoldRemote-TopBar-Location"></div>
|
|
141
363
|
<div class="retold-remote-topbar-info" id="RetoldRemote-TopBar-Info"></div>
|
|
142
364
|
<div class="retold-remote-topbar-actions">
|
|
365
|
+
<button class="retold-remote-topbar-btn retold-remote-topbar-addcoll-btn" id="RetoldRemote-TopBar-AddToCollectionBtn" onclick="pict.views['ContentEditor-TopBar'].addToCollection(event)" title="Add to collection">+★</button>
|
|
366
|
+
<button class="retold-remote-topbar-sidebar-toggle retold-remote-topbar-collections-btn" id="RetoldRemote-TopBar-CollectionsBtn" onclick="pict.views['ContentEditor-TopBar'].toggleCollections()" title="Toggle Collections panel (b)">★</button>
|
|
143
367
|
<button class="retold-remote-topbar-filter-btn" id="RetoldRemote-TopBar-FilterBtn" onclick="pict.views['ContentEditor-TopBar'].toggleFilterBar()" title="Toggle filter bar (/)">◢</button>
|
|
144
|
-
<button class="retold-remote-topbar-btn" onclick="pict.views['ContentEditor-Layout'].toggleSidebar()" title="Toggle Sidebar">☰</button>
|
|
145
368
|
</div>
|
|
146
369
|
</div>
|
|
147
370
|
`
|
|
@@ -168,12 +391,73 @@ class RetoldRemoteTopBarView extends libPictView
|
|
|
168
391
|
onAfterRender()
|
|
169
392
|
{
|
|
170
393
|
super.onAfterRender();
|
|
394
|
+
this.updateSidebarToggleIcon();
|
|
395
|
+
this.updateDFToggleIcon();
|
|
171
396
|
this.updateLocation();
|
|
172
397
|
this.updateInfo();
|
|
173
398
|
}
|
|
174
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Inject the SVG sidebar icon into the sidebar toggle button.
|
|
402
|
+
*/
|
|
403
|
+
updateSidebarToggleIcon()
|
|
404
|
+
{
|
|
405
|
+
let tmpBtn = document.getElementById('RetoldRemote-TopBar-SidebarToggle');
|
|
406
|
+
if (!tmpBtn)
|
|
407
|
+
{
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
let tmpIconProvider = this.pict.providers['RetoldRemote-Icons'];
|
|
412
|
+
if (tmpIconProvider)
|
|
413
|
+
{
|
|
414
|
+
tmpBtn.innerHTML = tmpIconProvider.getIcon('sidebar', 18);
|
|
415
|
+
}
|
|
416
|
+
else
|
|
417
|
+
{
|
|
418
|
+
tmpBtn.innerHTML = '☰';
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Inject the SVG expand icon into the distraction-free toggle button.
|
|
424
|
+
*/
|
|
425
|
+
updateDFToggleIcon()
|
|
426
|
+
{
|
|
427
|
+
let tmpBtn = document.getElementById('RetoldRemote-TopBar-DFToggle');
|
|
428
|
+
if (!tmpBtn)
|
|
429
|
+
{
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Four-corner expand icon
|
|
434
|
+
tmpBtn.innerHTML = '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">'
|
|
435
|
+
+ '<polyline points="15 3 21 3 21 9" />'
|
|
436
|
+
+ '<polyline points="9 21 3 21 3 15" />'
|
|
437
|
+
+ '<polyline points="21 15 21 21 15 21" />'
|
|
438
|
+
+ '<polyline points="3 9 3 3 9 3" />'
|
|
439
|
+
+ '</svg>';
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Toggle distraction-free mode via the GalleryNavigation provider.
|
|
444
|
+
*/
|
|
445
|
+
toggleDistractionFree()
|
|
446
|
+
{
|
|
447
|
+
let tmpNav = this.pict.providers['RetoldRemote-GalleryNavigation'];
|
|
448
|
+
if (tmpNav && tmpNav._toggleDistractionFree)
|
|
449
|
+
{
|
|
450
|
+
tmpNav._toggleDistractionFree();
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
175
454
|
/**
|
|
176
455
|
* Update the breadcrumb location display.
|
|
456
|
+
*
|
|
457
|
+
* When more than one folder deep, shows a hamburger button to the
|
|
458
|
+
* left of the home icon with a dropdown listing the intermediate
|
|
459
|
+
* path segments. The breadcrumb itself shows only
|
|
460
|
+
* [home] / [current folder].
|
|
177
461
|
*/
|
|
178
462
|
updateLocation()
|
|
179
463
|
{
|
|
@@ -186,25 +470,149 @@ class RetoldRemoteTopBarView extends libPictView
|
|
|
186
470
|
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
187
471
|
let tmpCurrentLocation = (this.pict.AppData.PictFileBrowser && this.pict.AppData.PictFileBrowser.CurrentLocation) || '';
|
|
188
472
|
|
|
473
|
+
// Build the home icon for the root crumb
|
|
474
|
+
let tmpIconProvider = this.pict.providers['RetoldRemote-Icons'];
|
|
475
|
+
let tmpHomeIcon = tmpIconProvider ? tmpIconProvider.getIcon('home', 16) : '/';
|
|
476
|
+
let tmpHomeCrumb = '<span class="retold-remote-topbar-home-crumb" onclick="pict.PictApplication.loadFileList(\'\')" title="Home">' + tmpHomeIcon + '</span>';
|
|
477
|
+
|
|
189
478
|
if (!tmpCurrentLocation)
|
|
190
479
|
{
|
|
191
|
-
tmpLocationEl.innerHTML = '<span class="retold-remote-topbar-location-
|
|
480
|
+
tmpLocationEl.innerHTML = '<span class="retold-remote-topbar-location-inner">' + tmpHomeCrumb + '</span>';
|
|
192
481
|
return;
|
|
193
482
|
}
|
|
194
483
|
|
|
195
484
|
let tmpParts = tmpCurrentLocation.split('/').filter((p) => p);
|
|
196
|
-
let tmpHTML = '<span class="retold-remote-topbar-location-crumb" onclick="pict.PictApplication.loadFileList(\'\')">/</span>';
|
|
197
485
|
|
|
198
|
-
|
|
486
|
+
// Shallow path (1 level): home / folder — no hamburger needed
|
|
487
|
+
if (tmpParts.length <= 1)
|
|
488
|
+
{
|
|
489
|
+
let tmpInner = tmpHomeCrumb;
|
|
490
|
+
for (let i = 0; i < tmpParts.length; i++)
|
|
491
|
+
{
|
|
492
|
+
let tmpPath = tmpParts.slice(0, i + 1).join('/');
|
|
493
|
+
tmpInner += '<span class="retold-remote-topbar-sep">/</span>';
|
|
494
|
+
tmpInner += '<span class="retold-remote-topbar-location-crumb" onclick="pict.PictApplication.loadFileList(\'' + tmpPath + '\')">' + tmpParts[i] + '</span>';
|
|
495
|
+
}
|
|
496
|
+
tmpLocationEl.innerHTML = '<span class="retold-remote-topbar-location-inner">' + tmpInner + '</span>';
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Deep path (2+ levels): show hamburger with intermediate folders
|
|
501
|
+
let tmpFolderIcon = tmpIconProvider ? tmpIconProvider.getIcon('folder', 16) : '';
|
|
502
|
+
let tmpHomeIconSmall = tmpIconProvider ? tmpIconProvider.getIcon('home', 16) : '/';
|
|
503
|
+
|
|
504
|
+
// Build dropdown items: home first, then each intermediate folder
|
|
505
|
+
let tmpDropdownHTML = '';
|
|
506
|
+
|
|
507
|
+
// Home item
|
|
508
|
+
tmpDropdownHTML += '<button class="retold-remote-topbar-overflow-item" onclick="pict.PictApplication.loadFileList(\'\'); pict.views[\'ContentEditor-TopBar\'].closeBreadcrumbDropdown();">';
|
|
509
|
+
tmpDropdownHTML += '<span class="retold-remote-topbar-overflow-item-icon">' + tmpHomeIconSmall + '</span>';
|
|
510
|
+
tmpDropdownHTML += '<span class="retold-remote-topbar-overflow-item-label">Home</span>';
|
|
511
|
+
tmpDropdownHTML += '</button>';
|
|
512
|
+
|
|
513
|
+
// Intermediate folders (all except the last segment, which is shown in the breadcrumb)
|
|
514
|
+
for (let i = 0; i < tmpParts.length - 1; i++)
|
|
199
515
|
{
|
|
200
516
|
let tmpPath = tmpParts.slice(0, i + 1).join('/');
|
|
201
|
-
|
|
202
|
-
|
|
517
|
+
tmpDropdownHTML += '<button class="retold-remote-topbar-overflow-item" onclick="pict.PictApplication.loadFileList(\'' + tmpPath + '\'); pict.views[\'ContentEditor-TopBar\'].closeBreadcrumbDropdown();">';
|
|
518
|
+
tmpDropdownHTML += '<span class="retold-remote-topbar-overflow-item-icon">' + tmpFolderIcon + '</span>';
|
|
519
|
+
tmpDropdownHTML += '<span class="retold-remote-topbar-overflow-item-label">' + tmpParts[i] + '</span>';
|
|
520
|
+
tmpDropdownHTML += '</button>';
|
|
203
521
|
}
|
|
204
522
|
|
|
523
|
+
// Assemble: [hamburger + dropdown] [inner: home / current folder]
|
|
524
|
+
let tmpLastPart = tmpParts[tmpParts.length - 1];
|
|
525
|
+
let tmpLastPath = tmpParts.join('/');
|
|
526
|
+
|
|
527
|
+
let tmpHTML = '';
|
|
528
|
+
// The overflow wrapper sits outside the truncation inner
|
|
529
|
+
tmpHTML += '<span class="retold-remote-topbar-breadcrumb-overflow">';
|
|
530
|
+
tmpHTML += '<button class="retold-remote-topbar-overflow-btn" onclick="pict.views[\'ContentEditor-TopBar\'].toggleBreadcrumbDropdown()" title="Navigate to parent folders">☰</button>';
|
|
531
|
+
tmpHTML += '<div class="retold-remote-topbar-overflow-dropdown" id="RetoldRemote-BreadcrumbDropdown">';
|
|
532
|
+
tmpHTML += tmpDropdownHTML;
|
|
533
|
+
tmpHTML += '</div>';
|
|
534
|
+
tmpHTML += '</span>';
|
|
535
|
+
// The visible crumbs: home / current-folder
|
|
536
|
+
tmpHTML += '<span class="retold-remote-topbar-location-inner">';
|
|
537
|
+
tmpHTML += tmpHomeCrumb;
|
|
538
|
+
tmpHTML += '<span class="retold-remote-topbar-sep">/</span>';
|
|
539
|
+
tmpHTML += '<span class="retold-remote-topbar-location-crumb" onclick="pict.PictApplication.loadFileList(\'' + tmpLastPath + '\')">' + tmpLastPart + '</span>';
|
|
540
|
+
tmpHTML += '</span>';
|
|
541
|
+
|
|
205
542
|
tmpLocationEl.innerHTML = tmpHTML;
|
|
206
543
|
}
|
|
207
544
|
|
|
545
|
+
/**
|
|
546
|
+
* Toggle the breadcrumb overflow dropdown.
|
|
547
|
+
*/
|
|
548
|
+
toggleBreadcrumbDropdown()
|
|
549
|
+
{
|
|
550
|
+
let tmpDropdown = document.getElementById('RetoldRemote-BreadcrumbDropdown');
|
|
551
|
+
if (!tmpDropdown)
|
|
552
|
+
{
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
let tmpIsOpen = tmpDropdown.classList.contains('open');
|
|
557
|
+
|
|
558
|
+
if (tmpIsOpen)
|
|
559
|
+
{
|
|
560
|
+
this.closeBreadcrumbDropdown();
|
|
561
|
+
}
|
|
562
|
+
else
|
|
563
|
+
{
|
|
564
|
+
tmpDropdown.classList.add('open');
|
|
565
|
+
|
|
566
|
+
// Close on outside click/tap
|
|
567
|
+
let tmpSelf = this;
|
|
568
|
+
let tmpCloseHandler = function(pEvent)
|
|
569
|
+
{
|
|
570
|
+
// Ignore clicks inside the dropdown or on the toggle button
|
|
571
|
+
if (tmpDropdown.contains(pEvent.target))
|
|
572
|
+
{
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
let tmpBtn = tmpDropdown.parentElement && tmpDropdown.parentElement.querySelector('.retold-remote-topbar-overflow-btn');
|
|
576
|
+
if (tmpBtn && tmpBtn.contains(pEvent.target))
|
|
577
|
+
{
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
tmpSelf.closeBreadcrumbDropdown();
|
|
581
|
+
document.removeEventListener('click', tmpCloseHandler, true);
|
|
582
|
+
document.removeEventListener('touchstart', tmpCloseHandler, true);
|
|
583
|
+
};
|
|
584
|
+
|
|
585
|
+
// Defer attaching so the current click doesn't immediately close it
|
|
586
|
+
setTimeout(function()
|
|
587
|
+
{
|
|
588
|
+
document.addEventListener('click', tmpCloseHandler, true);
|
|
589
|
+
document.addEventListener('touchstart', tmpCloseHandler, true);
|
|
590
|
+
}, 0);
|
|
591
|
+
|
|
592
|
+
// Store the handler so closeBreadcrumbDropdown can clean it up
|
|
593
|
+
this._breadcrumbCloseHandler = tmpCloseHandler;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Close the breadcrumb overflow dropdown.
|
|
599
|
+
*/
|
|
600
|
+
closeBreadcrumbDropdown()
|
|
601
|
+
{
|
|
602
|
+
let tmpDropdown = document.getElementById('RetoldRemote-BreadcrumbDropdown');
|
|
603
|
+
if (tmpDropdown)
|
|
604
|
+
{
|
|
605
|
+
tmpDropdown.classList.remove('open');
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (this._breadcrumbCloseHandler)
|
|
609
|
+
{
|
|
610
|
+
document.removeEventListener('click', this._breadcrumbCloseHandler, true);
|
|
611
|
+
document.removeEventListener('touchstart', this._breadcrumbCloseHandler, true);
|
|
612
|
+
this._breadcrumbCloseHandler = null;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
208
616
|
/**
|
|
209
617
|
* Toggle the filter bar visibility.
|
|
210
618
|
* If hidden, show it and focus the search box.
|
|
@@ -215,6 +623,12 @@ class RetoldRemoteTopBarView extends libPictView
|
|
|
215
623
|
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
216
624
|
tmpRemote.FilterBarVisible = !tmpRemote.FilterBarVisible;
|
|
217
625
|
|
|
626
|
+
// When hiding the filter bar, also close the advanced filter panel
|
|
627
|
+
if (!tmpRemote.FilterBarVisible)
|
|
628
|
+
{
|
|
629
|
+
tmpRemote.FilterPanelOpen = false;
|
|
630
|
+
}
|
|
631
|
+
|
|
218
632
|
let tmpGalleryView = this.pict.views['RetoldRemote-Gallery'];
|
|
219
633
|
if (tmpGalleryView)
|
|
220
634
|
{
|
|
@@ -299,18 +713,29 @@ class RetoldRemoteTopBarView extends libPictView
|
|
|
299
713
|
let tmpItem = tmpItems[tmpIndex];
|
|
300
714
|
if (tmpItem)
|
|
301
715
|
{
|
|
302
|
-
|
|
716
|
+
let tmpPos = (tmpIndex + 1) + '/' + tmpItems.length;
|
|
717
|
+
tmpInfoEl.textContent = tmpPos + ' \u00b7 ' + tmpItem.Name;
|
|
303
718
|
}
|
|
304
719
|
return;
|
|
305
720
|
}
|
|
306
721
|
|
|
722
|
+
let tmpItems = tmpRemote.GalleryItems || [];
|
|
723
|
+
let tmpIndex = tmpRemote.GalleryCursorIndex || 0;
|
|
724
|
+
let tmpCursorText = '';
|
|
725
|
+
|
|
726
|
+
if (tmpItems.length > 0)
|
|
727
|
+
{
|
|
728
|
+
tmpCursorText = (tmpIndex + 1) + '/' + tmpItems.length;
|
|
729
|
+
}
|
|
730
|
+
|
|
307
731
|
if (!tmpSummary)
|
|
308
732
|
{
|
|
309
|
-
tmpInfoEl.textContent =
|
|
733
|
+
tmpInfoEl.textContent = tmpCursorText;
|
|
310
734
|
return;
|
|
311
735
|
}
|
|
312
736
|
|
|
313
737
|
let tmpParts = [];
|
|
738
|
+
if (tmpCursorText) tmpParts.push(tmpCursorText);
|
|
314
739
|
if (tmpSummary.Folders > 0) tmpParts.push(tmpSummary.Folders + ' folders');
|
|
315
740
|
if (tmpSummary.Images > 0) tmpParts.push(tmpSummary.Images + ' images');
|
|
316
741
|
if (tmpSummary.Videos > 0) tmpParts.push(tmpSummary.Videos + ' videos');
|
|
@@ -320,6 +745,182 @@ class RetoldRemoteTopBarView extends libPictView
|
|
|
320
745
|
|
|
321
746
|
tmpInfoEl.textContent = tmpParts.join(' \u00b7 ');
|
|
322
747
|
}
|
|
748
|
+
|
|
749
|
+
// -- Collections Panel ------------------------------------------------
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Toggle the collections panel.
|
|
753
|
+
*/
|
|
754
|
+
toggleCollections()
|
|
755
|
+
{
|
|
756
|
+
let tmpManager = this.pict.providers['RetoldRemote-CollectionManager'];
|
|
757
|
+
if (tmpManager)
|
|
758
|
+
{
|
|
759
|
+
tmpManager.togglePanel();
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
/**
|
|
764
|
+
* Update the collections toggle button icon/state.
|
|
765
|
+
*/
|
|
766
|
+
updateCollectionsIcon()
|
|
767
|
+
{
|
|
768
|
+
let tmpBtn = document.getElementById('RetoldRemote-TopBar-CollectionsBtn');
|
|
769
|
+
if (!tmpBtn)
|
|
770
|
+
{
|
|
771
|
+
return;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
775
|
+
let tmpIconProvider = this.pict.providers['RetoldRemote-Icons'];
|
|
776
|
+
|
|
777
|
+
if (tmpRemote.CollectionsPanelOpen)
|
|
778
|
+
{
|
|
779
|
+
tmpBtn.classList.add('panel-open');
|
|
780
|
+
if (tmpIconProvider && typeof tmpIconProvider.getIcon === 'function')
|
|
781
|
+
{
|
|
782
|
+
tmpBtn.innerHTML = tmpIconProvider.getIcon('bookmark-filled', 16);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
else
|
|
786
|
+
{
|
|
787
|
+
tmpBtn.classList.remove('panel-open');
|
|
788
|
+
if (tmpIconProvider && typeof tmpIconProvider.getIcon === 'function')
|
|
789
|
+
{
|
|
790
|
+
tmpBtn.innerHTML = tmpIconProvider.getIcon('bookmark', 16);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
/**
|
|
796
|
+
* Add current file/folder to a collection.
|
|
797
|
+
* Quick-add: single click adds to last-used collection.
|
|
798
|
+
* If no last-used collection, opens the picker dropdown.
|
|
799
|
+
*
|
|
800
|
+
* @param {Event} pEvent - Click event
|
|
801
|
+
*/
|
|
802
|
+
addToCollection(pEvent)
|
|
803
|
+
{
|
|
804
|
+
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
805
|
+
let tmpManager = this.pict.providers['RetoldRemote-CollectionManager'];
|
|
806
|
+
if (!tmpManager)
|
|
807
|
+
{
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Quick-add: if we have a last-used collection, add directly
|
|
812
|
+
if (tmpRemote.LastUsedCollectionGUID)
|
|
813
|
+
{
|
|
814
|
+
let tmpAdded = tmpManager.addCurrentFileToCollection(tmpRemote.LastUsedCollectionGUID);
|
|
815
|
+
if (tmpAdded)
|
|
816
|
+
{
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Fall through to picker dropdown
|
|
822
|
+
this.showAddToCollectionDropdown(pEvent);
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
/**
|
|
826
|
+
* Show the add-to-collection picker dropdown.
|
|
827
|
+
*
|
|
828
|
+
* @param {Event} [pEvent] - Optional click event for positioning
|
|
829
|
+
*/
|
|
830
|
+
showAddToCollectionDropdown(pEvent)
|
|
831
|
+
{
|
|
832
|
+
let tmpSelf = this;
|
|
833
|
+
let tmpRemote = this.pict.AppData.RetoldRemote;
|
|
834
|
+
let tmpManager = this.pict.providers['RetoldRemote-CollectionManager'];
|
|
835
|
+
|
|
836
|
+
// Remove any existing dropdown
|
|
837
|
+
this._closeAddToCollectionDropdown();
|
|
838
|
+
|
|
839
|
+
let tmpBtn = document.getElementById('RetoldRemote-TopBar-AddToCollectionBtn');
|
|
840
|
+
if (!tmpBtn)
|
|
841
|
+
{
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Ensure we have the latest collections
|
|
846
|
+
tmpManager.fetchCollections(() =>
|
|
847
|
+
{
|
|
848
|
+
let tmpCollections = tmpRemote.Collections || [];
|
|
849
|
+
|
|
850
|
+
let tmpDropdown = document.createElement('div');
|
|
851
|
+
tmpDropdown.className = 'retold-remote-addcoll-dropdown';
|
|
852
|
+
tmpDropdown.id = 'RetoldRemote-AddToCollection-Dropdown';
|
|
853
|
+
|
|
854
|
+
// "New Collection" option
|
|
855
|
+
let tmpNewItem = document.createElement('button');
|
|
856
|
+
tmpNewItem.className = 'retold-remote-addcoll-dropdown-item retold-remote-addcoll-dropdown-new';
|
|
857
|
+
tmpNewItem.textContent = '+ New Collection...';
|
|
858
|
+
tmpNewItem.onclick = () =>
|
|
859
|
+
{
|
|
860
|
+
tmpSelf._closeAddToCollectionDropdown();
|
|
861
|
+
let tmpName = prompt('Collection name:');
|
|
862
|
+
if (tmpName && tmpName.trim())
|
|
863
|
+
{
|
|
864
|
+
tmpManager.createCollection(tmpName.trim(), (pError, pCollection) =>
|
|
865
|
+
{
|
|
866
|
+
if (!pError && pCollection)
|
|
867
|
+
{
|
|
868
|
+
tmpManager.addCurrentFileToCollection(pCollection.GUID);
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
};
|
|
873
|
+
tmpDropdown.appendChild(tmpNewItem);
|
|
874
|
+
|
|
875
|
+
// Existing collections
|
|
876
|
+
for (let i = 0; i < tmpCollections.length; i++)
|
|
877
|
+
{
|
|
878
|
+
let tmpCollection = tmpCollections[i];
|
|
879
|
+
let tmpItem = document.createElement('button');
|
|
880
|
+
tmpItem.className = 'retold-remote-addcoll-dropdown-item';
|
|
881
|
+
tmpItem.textContent = tmpCollection.Name || 'Untitled';
|
|
882
|
+
tmpItem.onclick = () =>
|
|
883
|
+
{
|
|
884
|
+
tmpSelf._closeAddToCollectionDropdown();
|
|
885
|
+
tmpManager.addCurrentFileToCollection(tmpCollection.GUID);
|
|
886
|
+
};
|
|
887
|
+
tmpDropdown.appendChild(tmpItem);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// Position relative to the button
|
|
891
|
+
tmpBtn.style.position = 'relative';
|
|
892
|
+
tmpBtn.appendChild(tmpDropdown);
|
|
893
|
+
|
|
894
|
+
// Close on outside click
|
|
895
|
+
setTimeout(() =>
|
|
896
|
+
{
|
|
897
|
+
document.addEventListener('click', tmpSelf._boundCloseDropdown = (pClickEvent) =>
|
|
898
|
+
{
|
|
899
|
+
if (!tmpDropdown.contains(pClickEvent.target) && pClickEvent.target !== tmpBtn)
|
|
900
|
+
{
|
|
901
|
+
tmpSelf._closeAddToCollectionDropdown();
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
}, 10);
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* Close the add-to-collection dropdown.
|
|
910
|
+
*/
|
|
911
|
+
_closeAddToCollectionDropdown()
|
|
912
|
+
{
|
|
913
|
+
let tmpDropdown = document.getElementById('RetoldRemote-AddToCollection-Dropdown');
|
|
914
|
+
if (tmpDropdown)
|
|
915
|
+
{
|
|
916
|
+
tmpDropdown.remove();
|
|
917
|
+
}
|
|
918
|
+
if (this._boundCloseDropdown)
|
|
919
|
+
{
|
|
920
|
+
document.removeEventListener('click', this._boundCloseDropdown);
|
|
921
|
+
this._boundCloseDropdown = null;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
323
924
|
}
|
|
324
925
|
|
|
325
926
|
RetoldRemoteTopBarView.default_configuration = _ViewConfiguration;
|