retold-remote 0.0.1

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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/css/retold-remote.css +83 -0
  3. package/html/codejar.js +511 -0
  4. package/html/index.html +23 -0
  5. package/package.json +68 -0
  6. package/server.js +43 -0
  7. package/source/Pict-Application-RetoldRemote-Configuration.json +7 -0
  8. package/source/Pict-Application-RetoldRemote.js +622 -0
  9. package/source/Pict-RetoldRemote-Bundle.js +14 -0
  10. package/source/cli/RetoldRemote-CLI-Program.js +15 -0
  11. package/source/cli/RetoldRemote-CLI-Run.js +3 -0
  12. package/source/cli/RetoldRemote-Server-Setup.js +257 -0
  13. package/source/cli/commands/RetoldRemote-Command-Serve.js +87 -0
  14. package/source/providers/Pict-Provider-GalleryFilterSort.js +597 -0
  15. package/source/providers/Pict-Provider-GalleryNavigation.js +819 -0
  16. package/source/providers/Pict-Provider-RetoldRemote.js +273 -0
  17. package/source/providers/Pict-Provider-RetoldRemoteIcons.js +640 -0
  18. package/source/providers/Pict-Provider-RetoldRemoteTheme.js +879 -0
  19. package/source/server/RetoldRemote-MediaService.js +536 -0
  20. package/source/server/RetoldRemote-PathRegistry.js +121 -0
  21. package/source/server/RetoldRemote-ThumbnailCache.js +89 -0
  22. package/source/server/RetoldRemote-ToolDetector.js +78 -0
  23. package/source/views/PictView-Remote-Gallery.js +1437 -0
  24. package/source/views/PictView-Remote-ImageViewer.js +363 -0
  25. package/source/views/PictView-Remote-Layout.js +420 -0
  26. package/source/views/PictView-Remote-MediaViewer.js +530 -0
  27. package/source/views/PictView-Remote-SettingsPanel.js +318 -0
  28. package/source/views/PictView-Remote-TopBar.js +206 -0
  29. package/web-application/codejar.js +511 -0
  30. package/web-application/css/retold-remote.css +83 -0
  31. package/web-application/index.html +23 -0
  32. package/web-application/js/pict.min.js +12 -0
  33. package/web-application/js/pict.min.js.map +1 -0
  34. package/web-application/retold-remote.compatible.js +5764 -0
  35. package/web-application/retold-remote.compatible.js.map +1 -0
  36. package/web-application/retold-remote.compatible.min.js +120 -0
  37. package/web-application/retold-remote.compatible.min.js.map +1 -0
  38. package/web-application/retold-remote.js +5763 -0
  39. package/web-application/retold-remote.js.map +1 -0
  40. package/web-application/retold-remote.min.js +120 -0
  41. package/web-application/retold-remote.min.js.map +1 -0
@@ -0,0 +1,640 @@
1
+ const libPictProvider = require('pict-provider');
2
+
3
+ const _DefaultProviderConfiguration =
4
+ {
5
+ "ProviderIdentifier": "RetoldRemote-Icons",
6
+ "AutoInitialize": true,
7
+ "AutoInitializeOrdinal": 0,
8
+ "AutoSolveWithApp": true,
9
+ "AutoSolveOrdinal": 0
10
+ };
11
+
12
+ // ====================================================================
13
+ // DEFAULT DARK-THEME COLOR PALETTE
14
+ //
15
+ // Designed for retold-remote's dark navy background (#16162B).
16
+ // Light strokes on dark fills — the inverse of the filebrowser palette.
17
+ // Stored as instance state so colors can be swapped at runtime.
18
+ // ====================================================================
19
+ const _DefaultColors =
20
+ {
21
+ Primary: '#C0C0DD', // Light purple-gray — main strokes
22
+ Accent: '#66C2B8', // Teal — accent details (matches app accent)
23
+ Muted: '#6A6A8A', // Muted purple-gray — secondary strokes
24
+ Light: '#2A2A4A', // Dark fill for file body backgrounds
25
+ WarmBeige: '#2E2E4E', // Dark fill for folders
26
+ TealTint: '#1E3A3A', // Dark teal-tinted fill
27
+ Lavender: '#2A2A48', // Dark lavender fill for image/video
28
+ AmberTint: '#3A3028', // Dark amber fill for audio
29
+ PdfFill: '#3A2028', // Dark reddish fill for PDF
30
+ PdfText: '#E06060' // Bright red for PDF label text
31
+ };
32
+
33
+ // ====================================================================
34
+ // EXTENSION-TO-ICON MAP
35
+ //
36
+ // Reused from pict-section-filebrowser. Pure string→string mapping
37
+ // with no color references — safe to share across themes.
38
+ // ====================================================================
39
+ let _BaseExtensionMap;
40
+ try
41
+ {
42
+ _BaseExtensionMap = require('pict-section-filebrowser/source/providers/Pict-Provider-FileBrowserIcons.js').ExtensionMap;
43
+ }
44
+ catch (pError)
45
+ {
46
+ // Fallback if the require path differs in production bundles
47
+ _BaseExtensionMap = {};
48
+ }
49
+
50
+ // Ensure we have a reasonable extension map even if the import failed
51
+ const _FallbackExtensionMap =
52
+ {
53
+ // Images
54
+ '.jpg': 'file-image', '.jpeg': 'file-image', '.png': 'file-image',
55
+ '.gif': 'file-image', '.svg': 'file-image', '.webp': 'file-image',
56
+ '.bmp': 'file-image', '.ico': 'file-image', '.tiff': 'file-image',
57
+ '.tif': 'file-image', '.heic': 'file-image', '.heif': 'file-image',
58
+ '.avif': 'file-image', '.raw': 'file-image',
59
+
60
+ // Documents / text
61
+ '.txt': 'file-text', '.md': 'file-text', '.rtf': 'file-text',
62
+ '.doc': 'file-text', '.docx': 'file-text',
63
+
64
+ // PDF
65
+ '.pdf': 'file-pdf',
66
+
67
+ // Spreadsheets
68
+ '.xls': 'file-spreadsheet', '.xlsx': 'file-spreadsheet',
69
+ '.csv': 'file-spreadsheet', '.ods': 'file-spreadsheet',
70
+
71
+ // Code
72
+ '.js': 'file-code', '.ts': 'file-code', '.jsx': 'file-code',
73
+ '.tsx': 'file-code', '.py': 'file-code', '.rb': 'file-code',
74
+ '.java': 'file-code', '.c': 'file-code', '.cpp': 'file-code',
75
+ '.h': 'file-code', '.go': 'file-code', '.rs': 'file-code',
76
+ '.swift': 'file-code', '.kt': 'file-code', '.scala': 'file-code',
77
+ '.sh': 'file-code', '.bash': 'file-code', '.zsh': 'file-code',
78
+ '.php': 'file-code', '.lua': 'file-code', '.r': 'file-code',
79
+ '.sql': 'file-code', '.pl': 'file-code',
80
+
81
+ // Web / markup
82
+ '.html': 'file-web', '.htm': 'file-web', '.css': 'file-web',
83
+ '.scss': 'file-web', '.less': 'file-web', '.xml': 'file-web',
84
+
85
+ // Config
86
+ '.json': 'file-config', '.yaml': 'file-config', '.yml': 'file-config',
87
+ '.toml': 'file-config', '.ini': 'file-config', '.env': 'file-config',
88
+ '.conf': 'file-config', '.cfg': 'file-config',
89
+
90
+ // Archives
91
+ '.zip': 'file-archive', '.tar': 'file-archive', '.gz': 'file-archive',
92
+ '.rar': 'file-archive', '.7z': 'file-archive', '.bz2': 'file-archive',
93
+ '.xz': 'file-archive', '.tgz': 'file-archive',
94
+
95
+ // Audio
96
+ '.mp3': 'file-audio', '.wav': 'file-audio', '.flac': 'file-audio',
97
+ '.ogg': 'file-audio', '.aac': 'file-audio', '.wma': 'file-audio',
98
+ '.m4a': 'file-audio', '.opus': 'file-audio', '.aiff': 'file-audio',
99
+
100
+ // Video
101
+ '.mp4': 'file-video', '.avi': 'file-video', '.mov': 'file-video',
102
+ '.mkv': 'file-video', '.webm': 'file-video', '.wmv': 'file-video',
103
+ '.flv': 'file-video', '.m4v': 'file-video',
104
+
105
+ // Ebooks
106
+ '.epub': 'file-text', '.mobi': 'file-text'
107
+ };
108
+
109
+ const _ExtensionMap = (Object.keys(_BaseExtensionMap).length > 0)
110
+ ? _BaseExtensionMap
111
+ : _FallbackExtensionMap;
112
+
113
+
114
+ /**
115
+ * Dark-theme SVG icon provider for retold-remote.
116
+ *
117
+ * Carries forward the same hand-drawn retro SVG shapes from
118
+ * pict-section-filebrowser's icon provider, recolored for dark
119
+ * backgrounds. Colors are instance state — call `setColors()` to
120
+ * swap the palette at runtime.
121
+ *
122
+ * Usage:
123
+ * let tmpIcons = pict.providers['RetoldRemote-Icons'];
124
+ * let tmpSVG = tmpIcons.getIcon('folder', 48);
125
+ * let tmpFileSVG = tmpIcons.getIconForEntry(fileEntry, 16);
126
+ * tmpIcons.setColors({ Accent: '#FF9900' });
127
+ */
128
+ class RetoldRemoteIconProvider extends libPictProvider
129
+ {
130
+ constructor(pFable, pOptions, pServiceHash)
131
+ {
132
+ super(pFable, pOptions, pServiceHash);
133
+
134
+ // Colors as instance state so they can be changed at runtime
135
+ this._colors = Object.assign({}, _DefaultColors);
136
+
137
+ // Build icon functions that reference this._colors via tmpSelf closure
138
+ this._icons = this._buildIconSet();
139
+
140
+ // Track any custom icons registered after construction
141
+ this._customIcons = {};
142
+
143
+ // Copy the extension map
144
+ this._extensionMap = Object.assign({}, _ExtensionMap);
145
+
146
+ this._cssInjected = false;
147
+ }
148
+
149
+ // ====================================================================
150
+ // ICON SET BUILDER
151
+ //
152
+ // Returns an object of icon functions. Each function closes over
153
+ // `tmpSelf` to read `this._colors` at call time, not at definition
154
+ // time. This means `setColors()` + `_rebuildIcons()` produces
155
+ // SVGs in the new palette.
156
+ // ====================================================================
157
+
158
+ _buildIconSet()
159
+ {
160
+ let tmpSelf = this;
161
+
162
+ return {
163
+ // ---- Folder icons ----
164
+ 'folder': (pSize) =>
165
+ {
166
+ let c = tmpSelf._colors;
167
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
168
+ + '<path d="M3.2 7.1V17.2C3.2 18.2 4 19.1 5.1 18.9L19.1 19.1C20 19.1 20.9 18.2 20.8 17.1V9.1C20.9 8 20.1 7.1 19 7.1H12.1L10.1 4.9H5.1C3.9 5 3.1 5.9 3.2 7.1Z" fill="' + c.WarmBeige + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
169
+ + '<path d="M3.2 9H20.8" stroke="' + c.Primary + '" stroke-width="1" opacity="0.3" />'
170
+ + '</svg>';
171
+ },
172
+
173
+ 'folder-open': (pSize) =>
174
+ {
175
+ let c = tmpSelf._colors;
176
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
177
+ + '<path d="M3.2 7.1V17.2C3.2 18.2 4 19.1 5.1 18.9L19.1 19.1C20 19.1 20.9 18.2 20.8 17.1V9.1C20.9 8 20.1 7.1 19 7.1H12.1L10.1 4.9H5.1C3.9 5 3.1 5.9 3.2 7.1Z" fill="' + c.WarmBeige + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
178
+ + '<path d="M3.2 10.2L5.8 17.8C6 18.4 6.6 18.9 7.2 18.9H19.8L22.1 11.2C22.3 10.6 21.8 10 21.2 10H5.2C4.6 10 4 10.4 3.8 11" stroke="' + c.Primary + '" stroke-width="1.5" fill="' + c.Light + '" stroke-linecap="round" stroke-linejoin="round" opacity="0.7" />'
179
+ + '</svg>';
180
+ },
181
+
182
+ // ---- Generic file ----
183
+ 'file': (pSize) =>
184
+ {
185
+ let c = tmpSelf._colors;
186
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
187
+ + '<path d="M14.1 2.1H6.2C5 2.2 4.1 3 4.1 4.1V20.1C4 21.2 5 22 6.1 21.9H18C19.1 22 20 21.1 19.9 19.9V8.1L14.1 2.1Z" fill="' + c.Light + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
188
+ + '<path d="M13.9 2.1V8.2H20" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
189
+ + '</svg>';
190
+ },
191
+
192
+ // ---- Text / document file ----
193
+ 'file-text': (pSize) =>
194
+ {
195
+ let c = tmpSelf._colors;
196
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
197
+ + '<path d="M14.1 2.1H6.2C5 2.2 4.1 3 4.1 4.1V20.1C4 21.2 5 22 6.1 21.9H18C19.1 22 20 21.1 19.9 19.9V8.1L14.1 2.1Z" fill="' + c.Light + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
198
+ + '<path d="M13.9 2.1V8.2H20" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
199
+ + '<line x1="8.1" y1="12.8" x2="15.9" y2="12.8" stroke="' + c.Muted + '" stroke-width="1.2" stroke-linecap="round" />'
200
+ + '<line x1="8.1" y1="15.8" x2="15.9" y2="15.8" stroke="' + c.Muted + '" stroke-width="1.2" stroke-linecap="round" />'
201
+ + '<line x1="8.1" y1="18.8" x2="12.2" y2="18.8" stroke="' + c.Muted + '" stroke-width="1.2" stroke-linecap="round" />'
202
+ + '</svg>';
203
+ },
204
+
205
+ // ---- Code file ----
206
+ 'file-code': (pSize) =>
207
+ {
208
+ let c = tmpSelf._colors;
209
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
210
+ + '<path d="M14.1 2.1H6.2C5 2.2 4.1 3 4.1 4.1V20.1C4 21.2 5 22 6.1 21.9H18C19.1 22 20 21.1 19.9 19.9V8.1L14.1 2.1Z" fill="' + c.Light + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
211
+ + '<path d="M13.9 2.1V8.2H20" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
212
+ + '<path d="M8.5 13.2L6.8 15.1L8.6 16.8" stroke="' + c.Accent + '" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" />'
213
+ + '<path d="M15.5 13.2L17.2 15.1L15.4 16.8" stroke="' + c.Accent + '" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" />'
214
+ + '<line x1="12.8" y1="12" x2="11.2" y2="18" stroke="' + c.Muted + '" stroke-width="1.2" stroke-linecap="round" />'
215
+ + '</svg>';
216
+ },
217
+
218
+ // ---- Image file ----
219
+ 'file-image': (pSize) =>
220
+ {
221
+ let c = tmpSelf._colors;
222
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
223
+ + '<rect x="3.1" y="3.2" width="17.8" height="17.7" rx="2" fill="' + c.Lavender + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
224
+ + '<circle cx="8.3" cy="8.7" r="1.8" fill="' + c.Accent + '" />'
225
+ + '<path d="M20.8 15.2L15.9 10.1L5.2 20.8" stroke="' + c.Primary + '" stroke-width="1.8" fill="none" stroke-linecap="round" stroke-linejoin="round" />'
226
+ + '</svg>';
227
+ },
228
+
229
+ // ---- PDF file ----
230
+ 'file-pdf': (pSize) =>
231
+ {
232
+ let c = tmpSelf._colors;
233
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
234
+ + '<path d="M14.1 2.1H6.2C5 2.2 4.1 3 4.1 4.1V20.1C4 21.2 5 22 6.1 21.9H18C19.1 22 20 21.1 19.9 19.9V8.1L14.1 2.1Z" fill="' + c.PdfFill + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
235
+ + '<path d="M13.9 2.1V8.2H20" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
236
+ + '<text x="8.2" y="16.8" font-family="sans-serif" font-weight="700" font-size="6.5" fill="' + c.PdfText + '" letter-spacing="-0.3">PDF</text>'
237
+ + '</svg>';
238
+ },
239
+
240
+ // ---- Spreadsheet file ----
241
+ 'file-spreadsheet': (pSize) =>
242
+ {
243
+ let c = tmpSelf._colors;
244
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
245
+ + '<path d="M14.1 2.1H6.2C5 2.2 4.1 3 4.1 4.1V20.1C4 21.2 5 22 6.1 21.9H18C19.1 22 20 21.1 19.9 19.9V8.1L14.1 2.1Z" fill="' + c.TealTint + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
246
+ + '<path d="M13.9 2.1V8.2H20" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
247
+ + '<rect x="7.2" y="11.1" width="9.8" height="7.8" rx="0.5" fill="none" stroke="' + c.Accent + '" stroke-width="1.2" />'
248
+ + '<line x1="7.2" y1="13.7" x2="17" y2="13.7" stroke="' + c.Accent + '" stroke-width="1" />'
249
+ + '<line x1="7.2" y1="16.3" x2="17" y2="16.3" stroke="' + c.Accent + '" stroke-width="1" />'
250
+ + '<line x1="10.9" y1="11.1" x2="10.9" y2="18.9" stroke="' + c.Accent + '" stroke-width="1" />'
251
+ + '</svg>';
252
+ },
253
+
254
+ // ---- Archive file ----
255
+ 'file-archive': (pSize) =>
256
+ {
257
+ let c = tmpSelf._colors;
258
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
259
+ + '<path d="M14.1 2.1H6.2C5 2.2 4.1 3 4.1 4.1V20.1C4 21.2 5 22 6.1 21.9H18C19.1 22 20 21.1 19.9 19.9V8.1L14.1 2.1Z" fill="' + c.WarmBeige + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
260
+ + '<path d="M13.9 2.1V8.2H20" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
261
+ + '<rect x="8.8" y="11.2" width="2.5" height="2" rx="0.4" fill="' + c.Primary + '" />'
262
+ + '<rect x="8.8" y="14.2" width="2.5" height="2" rx="0.4" fill="' + c.Primary + '" />'
263
+ + '<rect x="8.8" y="17.2" width="2.5" height="2" rx="0.4" fill="' + c.Primary + '" />'
264
+ + '</svg>';
265
+ },
266
+
267
+ // ---- Audio file ----
268
+ 'file-audio': (pSize) =>
269
+ {
270
+ let c = tmpSelf._colors;
271
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
272
+ + '<path d="M14.1 2.1H6.2C5 2.2 4.1 3 4.1 4.1V20.1C4 21.2 5 22 6.1 21.9H18C19.1 22 20 21.1 19.9 19.9V8.1L14.1 2.1Z" fill="' + c.AmberTint + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
273
+ + '<path d="M13.9 2.1V8.2H20" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
274
+ + '<circle cx="10.2" cy="16.8" r="2.1" fill="none" stroke="' + c.Accent + '" stroke-width="1.5" />'
275
+ + '<path d="M12.2 16.8V11.2L16.1 10.1" stroke="' + c.Accent + '" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none" />'
276
+ + '<circle cx="16.1" cy="15.3" r="1.4" fill="none" stroke="' + c.Accent + '" stroke-width="1.2" />'
277
+ + '</svg>';
278
+ },
279
+
280
+ // ---- Video file ----
281
+ 'file-video': (pSize) =>
282
+ {
283
+ let c = tmpSelf._colors;
284
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
285
+ + '<path d="M14.1 2.1H6.2C5 2.2 4.1 3 4.1 4.1V20.1C4 21.2 5 22 6.1 21.9H18C19.1 22 20 21.1 19.9 19.9V8.1L14.1 2.1Z" fill="' + c.Lavender + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
286
+ + '<path d="M13.9 2.1V8.2H20" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
287
+ + '<path d="M9.8 12.2V18.2L15.8 15.2L9.8 12.2Z" fill="' + c.Accent + '" stroke="' + c.Accent + '" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" />'
288
+ + '</svg>';
289
+ },
290
+
291
+ // ---- Web / markup file ----
292
+ 'file-web': (pSize) =>
293
+ {
294
+ let c = tmpSelf._colors;
295
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
296
+ + '<circle cx="12" cy="12" r="8.9" fill="' + c.TealTint + '" stroke="' + c.Primary + '" stroke-width="1.8" />'
297
+ + '<ellipse cx="12" cy="12" rx="4.1" ry="8.9" fill="none" stroke="' + c.Primary + '" stroke-width="1.2" />'
298
+ + '<line x1="3.1" y1="12" x2="20.9" y2="12" stroke="' + c.Primary + '" stroke-width="1" />'
299
+ + '<path d="M4.8 7.8C7 8.5 9.4 8.9 12 8.9C14.6 8.9 17 8.5 19.2 7.8" stroke="' + c.Primary + '" stroke-width="1" fill="none" />'
300
+ + '<path d="M4.8 16.2C7 15.5 9.4 15.1 12 15.1C14.6 15.1 17 15.5 19.2 16.2" stroke="' + c.Primary + '" stroke-width="1" fill="none" />'
301
+ + '</svg>';
302
+ },
303
+
304
+ // ---- Config / settings file ----
305
+ 'file-config': (pSize) =>
306
+ {
307
+ let c = tmpSelf._colors;
308
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
309
+ + '<path d="M14.1 2.1H6.2C5 2.2 4.1 3 4.1 4.1V20.1C4 21.2 5 22 6.1 21.9H18C19.1 22 20 21.1 19.9 19.9V8.1L14.1 2.1Z" fill="' + c.Light + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
310
+ + '<path d="M13.9 2.1V8.2H20" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
311
+ + '<circle cx="12" cy="15" r="2.8" fill="none" stroke="' + c.Muted + '" stroke-width="1.5" />'
312
+ + '<line x1="12" y1="11" x2="12" y2="12.2" stroke="' + c.Muted + '" stroke-width="1.3" stroke-linecap="round" />'
313
+ + '<line x1="12" y1="17.8" x2="12" y2="19" stroke="' + c.Muted + '" stroke-width="1.3" stroke-linecap="round" />'
314
+ + '<line x1="14.8" y1="13.2" x2="15.8" y2="12.6" stroke="' + c.Muted + '" stroke-width="1.3" stroke-linecap="round" />'
315
+ + '<line x1="8.2" y1="17" x2="9.2" y2="16.4" stroke="' + c.Muted + '" stroke-width="1.3" stroke-linecap="round" />'
316
+ + '<line x1="14.8" y1="16.8" x2="15.8" y2="17.4" stroke="' + c.Muted + '" stroke-width="1.3" stroke-linecap="round" />'
317
+ + '<line x1="8.2" y1="13" x2="9.2" y2="13.6" stroke="' + c.Muted + '" stroke-width="1.3" stroke-linecap="round" />'
318
+ + '</svg>';
319
+ },
320
+
321
+ // ---- UI Icons ----
322
+ 'home': (pSize) =>
323
+ {
324
+ let c = tmpSelf._colors;
325
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
326
+ + '<path d="M3.1 9.6L12 3.1L20.9 9.6V19.9C20.9 20.5 20.5 21 19.9 20.9H4.1C3.5 21 3 20.5 3.1 19.9V9.6Z" fill="' + c.TealTint + '" stroke="' + c.Accent + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
327
+ + '<rect x="9.2" y="14.1" width="5.6" height="6.9" rx="0.5" fill="' + c.Accent + '" />'
328
+ + '</svg>';
329
+ },
330
+
331
+ 'arrow-up': (pSize) =>
332
+ {
333
+ let c = tmpSelf._colors;
334
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
335
+ + '<path d="M12.1 19.1V5.1" stroke="' + c.Muted + '" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />'
336
+ + '<path d="M5.2 11.9L12.1 5.1L18.9 11.9" stroke="' + c.Muted + '" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round" />'
337
+ + '</svg>';
338
+ },
339
+
340
+ 'chevron-right': (pSize) =>
341
+ {
342
+ let c = tmpSelf._colors;
343
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
344
+ + '<path d="M9.2 6.1L14.8 12.1L9.1 17.9" stroke="' + c.Muted + '" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round" />'
345
+ + '</svg>';
346
+ },
347
+
348
+ 'chevron-down': (pSize) =>
349
+ {
350
+ let c = tmpSelf._colors;
351
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
352
+ + '<path d="M6.1 9.2L12.1 14.8L17.9 9.1" stroke="' + c.Muted + '" stroke-width="2.5" fill="none" stroke-linecap="round" stroke-linejoin="round" />'
353
+ + '</svg>';
354
+ },
355
+
356
+ 'search': (pSize) =>
357
+ {
358
+ let c = tmpSelf._colors;
359
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
360
+ + '<circle cx="10.8" cy="10.8" r="6.8" stroke="' + c.Primary + '" stroke-width="1.8" />'
361
+ + '<line x1="15.9" y1="16.1" x2="20.8" y2="20.8" stroke="' + c.Primary + '" stroke-width="2" stroke-linecap="round" />'
362
+ + '</svg>';
363
+ },
364
+
365
+ 'sort-asc': (pSize) =>
366
+ {
367
+ let c = tmpSelf._colors;
368
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
369
+ + '<path d="M12 4.2L7.2 10.8H16.8L12 4.2Z" fill="' + c.Primary + '" />'
370
+ + '<path d="M12 19.8L7.2 13.2H16.8L12 19.8Z" fill="' + c.Muted + '" opacity="0.35" />'
371
+ + '</svg>';
372
+ },
373
+
374
+ 'sort-desc': (pSize) =>
375
+ {
376
+ let c = tmpSelf._colors;
377
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
378
+ + '<path d="M12 4.2L7.2 10.8H16.8L12 4.2Z" fill="' + c.Muted + '" opacity="0.35" />'
379
+ + '<path d="M12 19.8L7.2 13.2H16.8L12 19.8Z" fill="' + c.Primary + '" />'
380
+ + '</svg>';
381
+ },
382
+
383
+ // ====================================================================
384
+ // NEW MEDIA-SPECIFIC ICONS
385
+ // Not in the filebrowser set — standalone shapes for viewer fallbacks.
386
+ // ====================================================================
387
+
388
+ // ---- Standalone music note (audio viewer fallback) ----
389
+ 'music-note': (pSize) =>
390
+ {
391
+ let c = tmpSelf._colors;
392
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
393
+ + '<circle cx="8.2" cy="17.8" r="3.1" fill="' + c.AmberTint + '" stroke="' + c.Accent + '" stroke-width="1.8" />'
394
+ + '<path d="M11.2 17.8V5.2L19.1 3.1V14.8" stroke="' + c.Accent + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" fill="none" />'
395
+ + '<circle cx="16.1" cy="14.8" r="2.4" fill="' + c.AmberTint + '" stroke="' + c.Accent + '" stroke-width="1.5" />'
396
+ + '</svg>';
397
+ },
398
+
399
+ // ---- Film strip / video placeholder (video viewer fallback) ----
400
+ 'film-strip': (pSize) =>
401
+ {
402
+ let c = tmpSelf._colors;
403
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
404
+ + '<rect x="3" y="4" width="18" height="16" rx="2" stroke="' + c.Primary + '" stroke-width="1.8" fill="' + c.Lavender + '" />'
405
+ + '<path d="M9.5 8.5V15.5L16 12L9.5 8.5Z" fill="' + c.Accent + '" stroke="' + c.Accent + '" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" />'
406
+ + '</svg>';
407
+ },
408
+
409
+ // ---- Empty gallery / folder placeholder ----
410
+ 'gallery-empty': (pSize) =>
411
+ {
412
+ let c = tmpSelf._colors;
413
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
414
+ + '<path d="M3.2 7.1V17.2C3.2 18.2 4 19.1 5.1 18.9L19.1 19.1C20 19.1 20.9 18.2 20.8 17.1V9.1C20.9 8 20.1 7.1 19 7.1H12.1L10.1 4.9H5.1C3.9 5 3.1 5.9 3.2 7.1Z" fill="' + c.WarmBeige + '" stroke="' + c.Muted + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
415
+ + '<line x1="9" y1="11" x2="15" y2="17" stroke="' + c.Muted + '" stroke-width="1.2" stroke-linecap="round" opacity="0.5" />'
416
+ + '<line x1="15" y1="11" x2="9" y2="17" stroke="' + c.Muted + '" stroke-width="1.2" stroke-linecap="round" opacity="0.5" />'
417
+ + '</svg>';
418
+ },
419
+
420
+ // ---- Document / generic file fallback (viewer fallback) ----
421
+ 'document-large': (pSize) =>
422
+ {
423
+ let c = tmpSelf._colors;
424
+ return '<svg width="' + pSize + '" height="' + pSize + '" viewBox="0 0 24 24" fill="none">'
425
+ + '<path d="M14.1 2.1H6.2C5 2.2 4.1 3 4.1 4.1V20.1C4 21.2 5 22 6.1 21.9H18C19.1 22 20 21.1 19.9 19.9V8.1L14.1 2.1Z" fill="' + c.Light + '" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
426
+ + '<path d="M13.9 2.1V8.2H20" stroke="' + c.Primary + '" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />'
427
+ + '<line x1="8.1" y1="13" x2="15.9" y2="13" stroke="' + c.Muted + '" stroke-width="1.2" stroke-linecap="round" />'
428
+ + '<line x1="8.1" y1="16" x2="15.9" y2="16" stroke="' + c.Muted + '" stroke-width="1.2" stroke-linecap="round" />'
429
+ + '<line x1="8.1" y1="19" x2="12" y2="19" stroke="' + c.Muted + '" stroke-width="1.2" stroke-linecap="round" />'
430
+ + '</svg>';
431
+ }
432
+ };
433
+ }
434
+
435
+ // ====================================================================
436
+ // PUBLIC API
437
+ // ====================================================================
438
+
439
+ /**
440
+ * Get an SVG icon string by name.
441
+ *
442
+ * @param {string} pName - Icon name (e.g. 'folder', 'file-code')
443
+ * @param {number} [pSize=16] - Pixel size
444
+ * @returns {string} SVG string, or empty string if not found
445
+ */
446
+ getIcon(pName, pSize)
447
+ {
448
+ let tmpSize = pSize || 16;
449
+
450
+ // Check custom icons first, then built-in
451
+ let tmpIconFn = this._customIcons[pName] || this._icons[pName];
452
+
453
+ if (typeof tmpIconFn === 'function')
454
+ {
455
+ return tmpIconFn(tmpSize);
456
+ }
457
+
458
+ return '';
459
+ }
460
+
461
+ /**
462
+ * Get an SVG icon string for a file entry based on its type and extension.
463
+ *
464
+ * @param {Object} pEntry - File entry with Type, Extension, Icon properties
465
+ * @param {number} [pSize=16] - Pixel size
466
+ * @returns {string} SVG string
467
+ */
468
+ getIconForEntry(pEntry, pSize)
469
+ {
470
+ if (!pEntry)
471
+ {
472
+ return '';
473
+ }
474
+
475
+ // If the entry has an explicit Icon that looks like SVG, use it directly
476
+ if (pEntry.Icon && typeof pEntry.Icon === 'string' && pEntry.Icon.indexOf('<svg') === 0)
477
+ {
478
+ return pEntry.Icon;
479
+ }
480
+
481
+ let tmpSize = pSize || 16;
482
+
483
+ // Folder
484
+ if (pEntry.Type === 'folder')
485
+ {
486
+ return this.getIcon('folder', tmpSize);
487
+ }
488
+
489
+ // Lookup by extension
490
+ let tmpExt = (pEntry.Extension || '').toLowerCase();
491
+ if (tmpExt && this._extensionMap[tmpExt])
492
+ {
493
+ return this.getIcon(this._extensionMap[tmpExt], tmpSize);
494
+ }
495
+
496
+ // Default: generic file
497
+ return this.getIcon('file', tmpSize);
498
+ }
499
+
500
+ /**
501
+ * Get a UI icon by name.
502
+ *
503
+ * @param {string} pName - UI icon name
504
+ * @param {number} [pSize=16] - Pixel size
505
+ * @returns {string} SVG string
506
+ */
507
+ getUIIcon(pName, pSize)
508
+ {
509
+ return this.getIcon(pName, pSize || 16);
510
+ }
511
+
512
+ /**
513
+ * Register a custom icon. Custom icons take priority over built-in
514
+ * icons and survive `setColors()` rebuilds.
515
+ *
516
+ * @param {string} pName - Icon name
517
+ * @param {Function} pIconFunction - Function(pSize) => SVG string
518
+ * @returns {boolean} True if registered successfully
519
+ */
520
+ registerIcon(pName, pIconFunction)
521
+ {
522
+ if (!pName || typeof pIconFunction !== 'function')
523
+ {
524
+ return false;
525
+ }
526
+
527
+ this._customIcons[pName] = pIconFunction;
528
+ return true;
529
+ }
530
+
531
+ /**
532
+ * Register a file extension mapping.
533
+ *
534
+ * @param {string} pExtension - Extension including dot (e.g. '.vue')
535
+ * @param {string} pIconName - Icon name to use
536
+ * @returns {boolean} True if registered successfully
537
+ */
538
+ registerExtension(pExtension, pIconName)
539
+ {
540
+ if (!pExtension || !pIconName)
541
+ {
542
+ return false;
543
+ }
544
+
545
+ this._extensionMap[pExtension.toLowerCase()] = pIconName;
546
+ return true;
547
+ }
548
+
549
+ /**
550
+ * Override one or more colors and rebuild the built-in icon set.
551
+ * Custom icons registered via `registerIcon()` are preserved.
552
+ *
553
+ * @param {Object} pColorOverrides - Partial color palette (e.g. { Accent: '#FF9900' })
554
+ */
555
+ setColors(pColorOverrides)
556
+ {
557
+ if (!pColorOverrides || typeof pColorOverrides !== 'object')
558
+ {
559
+ return;
560
+ }
561
+
562
+ Object.assign(this._colors, pColorOverrides);
563
+ this._icons = this._buildIconSet();
564
+ }
565
+
566
+ /**
567
+ * Get the current color palette.
568
+ *
569
+ * @returns {Object} Copy of the active color palette
570
+ */
571
+ getColors()
572
+ {
573
+ return Object.assign({}, this._colors);
574
+ }
575
+
576
+ /**
577
+ * Get the full list of registered icon names.
578
+ *
579
+ * @returns {Array<string>} Array of icon names
580
+ */
581
+ getIconNames()
582
+ {
583
+ let tmpNames = Object.keys(this._icons);
584
+ let tmpCustomKeys = Object.keys(this._customIcons);
585
+ for (let i = 0; i < tmpCustomKeys.length; i++)
586
+ {
587
+ if (tmpNames.indexOf(tmpCustomKeys[i]) < 0)
588
+ {
589
+ tmpNames.push(tmpCustomKeys[i]);
590
+ }
591
+ }
592
+ return tmpNames;
593
+ }
594
+
595
+ /**
596
+ * Get a copy of the extension map.
597
+ *
598
+ * @returns {Object} Extension-to-icon name map
599
+ */
600
+ getExtensionMap()
601
+ {
602
+ return Object.assign({}, this._extensionMap);
603
+ }
604
+
605
+ /**
606
+ * Inject CSS classes for icon sizing into the pict CSSMap.
607
+ */
608
+ injectCSS()
609
+ {
610
+ if (this._cssInjected)
611
+ {
612
+ return;
613
+ }
614
+
615
+ if (this.pict && this.pict.CSSMap)
616
+ {
617
+ this.pict.CSSMap.addCSS('RetoldRemoteIcons',
618
+ '.retold-remote-icon { display: inline-flex; align-items: center; justify-content: center; vertical-align: middle; }\n'
619
+ + '.retold-remote-icon svg { display: block; }\n'
620
+ + '.retold-remote-icon-sm svg { width: 16px; height: 16px; }\n'
621
+ + '.retold-remote-icon-md svg { width: 48px; height: 48px; }\n'
622
+ + '.retold-remote-icon-lg svg { width: 64px; height: 64px; }\n'
623
+ + '.retold-remote-icon-xl svg { width: 96px; height: 96px; }\n'
624
+ );
625
+ this._cssInjected = true;
626
+ }
627
+ }
628
+
629
+ onAfterInitialize()
630
+ {
631
+ this.injectCSS();
632
+ return super.onAfterInitialize();
633
+ }
634
+ }
635
+
636
+ module.exports = RetoldRemoteIconProvider;
637
+
638
+ module.exports.default_configuration = _DefaultProviderConfiguration;
639
+
640
+ module.exports.DefaultColors = _DefaultColors;