@memberjunction/ng-file-storage 6.2.0-edge.0 → 6.2.0-edge.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 (36) hide show
  1. package/dist/lib/admin/storage-admin-dialog.component.d.ts +1 -1
  2. package/dist/lib/admin/storage-admin-dialog.component.js +2 -2
  3. package/dist/lib/admin/storage-admin-dialog.component.js.map +1 -1
  4. package/dist/lib/category-tree/category-tree.d.ts +83 -9
  5. package/dist/lib/category-tree/category-tree.d.ts.map +1 -1
  6. package/dist/lib/category-tree/category-tree.js +192 -50
  7. package/dist/lib/category-tree/category-tree.js.map +1 -1
  8. package/dist/lib/file-browser/file-browser.component.d.ts +30 -4
  9. package/dist/lib/file-browser/file-browser.component.d.ts.map +1 -1
  10. package/dist/lib/file-browser/file-browser.component.js +68 -16
  11. package/dist/lib/file-browser/file-browser.component.js.map +1 -1
  12. package/dist/lib/file-browser/file-grid.component.d.ts +404 -58
  13. package/dist/lib/file-browser/file-grid.component.d.ts.map +1 -1
  14. package/dist/lib/file-browser/file-grid.component.js +1040 -396
  15. package/dist/lib/file-browser/file-grid.component.js.map +1 -1
  16. package/dist/lib/file-browser/folder-tree.component.d.ts +65 -9
  17. package/dist/lib/file-browser/folder-tree.component.d.ts.map +1 -1
  18. package/dist/lib/file-browser/folder-tree.component.js +166 -58
  19. package/dist/lib/file-browser/folder-tree.component.js.map +1 -1
  20. package/dist/lib/file-browser/storage-providers-list.component.d.ts +55 -7
  21. package/dist/lib/file-browser/storage-providers-list.component.d.ts.map +1 -1
  22. package/dist/lib/file-browser/storage-providers-list.component.js +124 -34
  23. package/dist/lib/file-browser/storage-providers-list.component.js.map +1 -1
  24. package/dist/lib/file-upload/file-upload.d.ts +22 -2
  25. package/dist/lib/file-upload/file-upload.d.ts.map +1 -1
  26. package/dist/lib/file-upload/file-upload.js +40 -10
  27. package/dist/lib/file-upload/file-upload.js.map +1 -1
  28. package/dist/lib/files-grid/files-grid.d.ts +28 -4
  29. package/dist/lib/files-grid/files-grid.d.ts.map +1 -1
  30. package/dist/lib/files-grid/files-grid.js +71 -23
  31. package/dist/lib/files-grid/files-grid.js.map +1 -1
  32. package/dist/lib/record-attachments/record-attachments.component.d.ts +15 -7
  33. package/dist/lib/record-attachments/record-attachments.component.d.ts.map +1 -1
  34. package/dist/lib/record-attachments/record-attachments.component.js +34 -18
  35. package/dist/lib/record-attachments/record-attachments.component.js.map +1 -1
  36. package/package.json +13 -13
@@ -11,7 +11,7 @@ import * as i4 from "@memberjunction/ng-ui-components";
11
11
  const _forTrack0 = ($index, $item) => $item.path;
12
12
  const _forTrack1 = ($index, $item) => $item.account.ID;
13
13
  const _forTrack2 = ($index, $item) => $item.accountID;
14
- const _forTrack3 = ($index, $item) => $item.key;
14
+ const _forTrack3 = ($index, $item) => $item.Key;
15
15
  function FileGridComponent_Conditional_1_For_10_Conditional_2_Template(rf, ctx) { if (rf & 1) {
16
16
  i0.ɵɵelementStart(0, "span", 43);
17
17
  i0.ɵɵtext(1, "/");
@@ -195,7 +195,7 @@ function FileGridComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
195
195
  i0.ɵɵadvance(2);
196
196
  i0.ɵɵtextInterpolate2("", ctx_r1.selectedItems.length, " item", ctx_r1.selectedItems.length === 1 ? "" : "s", " selected");
197
197
  i0.ɵɵadvance(2);
198
- i0.ɵɵconditional(ctx_r1.selectedItems.length === 1 && ((tmp_3_0 = ctx_r1.getSelectedItem()) == null ? null : tmp_3_0.type) === "file" ? 7 : -1);
198
+ i0.ɵɵconditional(ctx_r1.selectedItems.length === 1 && ((tmp_3_0 = ctx_r1.getSelectedItem()) == null ? null : tmp_3_0.Type) === "file" ? 7 : -1);
199
199
  i0.ɵɵadvance();
200
200
  i0.ɵɵconditional(ctx_r1.selectedItems.length === 1 ? 8 : -1);
201
201
  } }
@@ -341,13 +341,13 @@ function FileGridComponent_Conditional_3_Conditional_22_Template(rf, ctx) { if (
341
341
  } if (rf & 2) {
342
342
  const ctx_r1 = i0.ɵɵnextContext(2);
343
343
  i0.ɵɵadvance(4);
344
- i0.ɵɵtextInterpolate(ctx_r1.multiProviderSearchResults.totalResultsReturned);
344
+ i0.ɵɵtextInterpolate(ctx_r1.multiProviderSearchResults.TotalResultsReturned);
345
345
  i0.ɵɵadvance();
346
- i0.ɵɵtextInterpolate1(" result", ctx_r1.multiProviderSearchResults.totalResultsReturned === 1 ? "" : "s", " found ");
346
+ i0.ɵɵtextInterpolate1(" result", ctx_r1.multiProviderSearchResults.TotalResultsReturned === 1 ? "" : "s", " found ");
347
347
  i0.ɵɵadvance(2);
348
- i0.ɵɵtextInterpolate2(" (", ctx_r1.multiProviderSearchResults.successfulAccounts, " account", ctx_r1.multiProviderSearchResults.successfulAccounts === 1 ? "" : "s", " searched) ");
348
+ i0.ɵɵtextInterpolate2(" (", ctx_r1.multiProviderSearchResults.SuccessfulAccounts, " account", ctx_r1.multiProviderSearchResults.SuccessfulAccounts === 1 ? "" : "s", " searched) ");
349
349
  i0.ɵɵadvance(5);
350
- i0.ɵɵrepeater(ctx_r1.multiProviderSearchResults.accountResults);
350
+ i0.ɵɵrepeater(ctx_r1.multiProviderSearchResults.AccountResults);
351
351
  } }
352
352
  function FileGridComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
353
353
  const _r11 = i0.ɵɵgetCurrentView();
@@ -447,12 +447,12 @@ function FileGridComponent_Conditional_6_For_2_Template(rf, ctx) { if (rf & 1) {
447
447
  } if (rf & 2) {
448
448
  const item_r19 = ctx.$implicit;
449
449
  const ctx_r1 = i0.ɵɵnextContext(2);
450
- i0.ɵɵclassProp("selected", ctx_r1.selectedItems.includes(item_r19.key));
450
+ i0.ɵɵclassProp("selected", ctx_r1.selectedItems.includes(item_r19.Key));
451
451
  i0.ɵɵproperty("title", item_r19.name);
452
452
  i0.ɵɵadvance(3);
453
- i0.ɵɵproperty("checked", ctx_r1.selectedItems.includes(item_r19.key));
453
+ i0.ɵɵproperty("checked", ctx_r1.selectedItems.includes(item_r19.Key));
454
454
  i0.ɵɵadvance(2);
455
- i0.ɵɵconditional(item_r19.type === "file" ? 5 : -1);
455
+ i0.ɵɵconditional(item_r19.Type === "file" ? 5 : -1);
456
456
  i0.ɵɵadvance();
457
457
  i0.ɵɵclassMap(ctx_r1.getItemColorClass(item_r19));
458
458
  i0.ɵɵadvance();
@@ -460,9 +460,9 @@ function FileGridComponent_Conditional_6_For_2_Template(rf, ctx) { if (rf & 1) {
460
460
  i0.ɵɵadvance(3);
461
461
  i0.ɵɵtextInterpolate(item_r19.name);
462
462
  i0.ɵɵadvance(3);
463
- i0.ɵɵtextInterpolate(item_r19.type === "folder" ? "Folder" : ctx_r1.formatFileSize(item_r19.size));
463
+ i0.ɵɵtextInterpolate(item_r19.Type === "folder" ? "Folder" : ctx_r1.formatFileSize(item_r19.Size));
464
464
  i0.ɵɵadvance(2);
465
- i0.ɵɵtextInterpolate(ctx_r1.formatDate(item_r19.lastModified));
465
+ i0.ɵɵtextInterpolate(ctx_r1.formatDate(item_r19.LastModified));
466
466
  } }
467
467
  function FileGridComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
468
468
  i0.ɵɵelementStart(0, "div", 6);
@@ -516,9 +516,9 @@ function FileGridComponent_Conditional_7_For_24_Template(rf, ctx) { if (rf & 1)
516
516
  } if (rf & 2) {
517
517
  const item_r23 = ctx.$implicit;
518
518
  const ctx_r1 = i0.ɵɵnextContext(2);
519
- i0.ɵɵclassProp("selected", ctx_r1.selectedItems.includes(item_r23.key));
519
+ i0.ɵɵclassProp("selected", ctx_r1.selectedItems.includes(item_r23.Key));
520
520
  i0.ɵɵadvance(2);
521
- i0.ɵɵproperty("checked", ctx_r1.selectedItems.includes(item_r23.key));
521
+ i0.ɵɵproperty("checked", ctx_r1.selectedItems.includes(item_r23.Key));
522
522
  i0.ɵɵadvance(3);
523
523
  i0.ɵɵclassMap(ctx_r1.getItemColorClass(item_r23));
524
524
  i0.ɵɵadvance();
@@ -530,11 +530,11 @@ function FileGridComponent_Conditional_7_For_24_Template(rf, ctx) { if (rf & 1)
530
530
  i0.ɵɵadvance(3);
531
531
  i0.ɵɵtextInterpolate(ctx_r1.getFileType(item_r23));
532
532
  i0.ɵɵadvance(3);
533
- i0.ɵɵtextInterpolate(item_r23.type === "folder" ? "\u2014" : ctx_r1.formatFileSize(item_r23.size));
533
+ i0.ɵɵtextInterpolate(item_r23.Type === "folder" ? "\u2014" : ctx_r1.formatFileSize(item_r23.Size));
534
534
  i0.ɵɵadvance(3);
535
- i0.ɵɵtextInterpolate(ctx_r1.formatDate(item_r23.lastModified));
535
+ i0.ɵɵtextInterpolate(ctx_r1.formatDate(item_r23.LastModified));
536
536
  i0.ɵɵadvance(3);
537
- i0.ɵɵconditional(item_r23.type === "file" ? 20 : -1);
537
+ i0.ɵɵconditional(item_r23.Type === "file" ? 20 : -1);
538
538
  } }
539
539
  function FileGridComponent_Conditional_7_Template(rf, ctx) { if (rf & 1) {
540
540
  const _r21 = i0.ɵɵgetCurrentView();
@@ -775,7 +775,7 @@ function FileGridComponent_Conditional_12_Template(rf, ctx) { if (rf & 1) {
775
775
  i0.ɵɵadvance(3);
776
776
  i0.ɵɵtextInterpolate(ctx_r1.previewItem.name);
777
777
  i0.ɵɵadvance(2);
778
- i0.ɵɵtextInterpolate2("", ctx_r1.formatFileSize(ctx_r1.previewItem.size), " \u2022 ", ctx_r1.formatDate(ctx_r1.previewItem.lastModified));
778
+ i0.ɵɵtextInterpolate2("", ctx_r1.formatFileSize(ctx_r1.previewItem.Size), " \u2022 ", ctx_r1.formatDate(ctx_r1.previewItem.LastModified));
779
779
  i0.ɵɵadvance(9);
780
780
  i0.ɵɵconditional(ctx_r1.isLoadingPreview ? 19 : ctx_r1.previewUrl ? 20 : 21);
781
781
  } }
@@ -871,11 +871,11 @@ function FileGridComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
871
871
  } if (rf & 2) {
872
872
  const ctx_r1 = i0.ɵɵnextContext();
873
873
  i0.ɵɵadvance(4);
874
- i0.ɵɵtextInterpolate1("Delete ", ctx_r1.itemToDelete.type === "folder" ? "Folder" : "File", "?");
874
+ i0.ɵɵtextInterpolate1("Delete ", ctx_r1.itemToDelete.Type === "folder" ? "Folder" : "File", "?");
875
875
  i0.ɵɵadvance(7);
876
876
  i0.ɵɵtextInterpolate(ctx_r1.itemToDelete.name);
877
877
  i0.ɵɵadvance(2);
878
- i0.ɵɵconditional(ctx_r1.itemToDelete.type === "folder" ? 13 : -1);
878
+ i0.ɵɵconditional(ctx_r1.itemToDelete.Type === "folder" ? 13 : -1);
879
879
  i0.ɵɵadvance(2);
880
880
  i0.ɵɵproperty("disabled", ctx_r1.isDeleting);
881
881
  i0.ɵɵadvance(2);
@@ -921,7 +921,7 @@ function FileGridComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
921
921
  } if (rf & 2) {
922
922
  const ctx_r1 = i0.ɵɵnextContext();
923
923
  i0.ɵɵadvance(4);
924
- i0.ɵɵtextInterpolate1("Rename ", ctx_r1.itemToRename.type === "folder" ? "Folder" : "File");
924
+ i0.ɵɵtextInterpolate1("Rename ", ctx_r1.itemToRename.Type === "folder" ? "Folder" : "File");
925
925
  i0.ɵɵadvance(6);
926
926
  i0.ɵɵtwoWayProperty("ngModel", ctx_r1.newItemName);
927
927
  i0.ɵɵproperty("disabled", ctx_r1.isRenaming);
@@ -975,7 +975,7 @@ function FileGridComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
975
975
  } if (rf & 2) {
976
976
  const ctx_r1 = i0.ɵɵnextContext();
977
977
  i0.ɵɵadvance(4);
978
- i0.ɵɵtextInterpolate1("Copy ", ctx_r1.itemToCopy.type === "folder" ? "Folder" : "File");
978
+ i0.ɵɵtextInterpolate1("Copy ", ctx_r1.itemToCopy.Type === "folder" ? "Folder" : "File");
979
979
  i0.ɵɵadvance(7);
980
980
  i0.ɵɵtextInterpolate(ctx_r1.itemToCopy.name);
981
981
  i0.ɵɵadvance(3);
@@ -1031,7 +1031,7 @@ function FileGridComponent_Conditional_17_Template(rf, ctx) { if (rf & 1) {
1031
1031
  } if (rf & 2) {
1032
1032
  const ctx_r1 = i0.ɵɵnextContext();
1033
1033
  i0.ɵɵadvance(4);
1034
- i0.ɵɵtextInterpolate1("Move ", ctx_r1.itemToMove.type === "folder" ? "Folder" : "File");
1034
+ i0.ɵɵtextInterpolate1("Move ", ctx_r1.itemToMove.Type === "folder" ? "Folder" : "File");
1035
1035
  i0.ɵɵadvance(7);
1036
1036
  i0.ɵɵtextInterpolate(ctx_r1.itemToMove.name);
1037
1037
  i0.ɵɵadvance(3);
@@ -1160,28 +1160,76 @@ export class FileGridComponent {
1160
1160
  /**
1161
1161
  * The storage account to list files from (includes provider details)
1162
1162
  */
1163
- account = null;
1163
+ Account = null;
1164
+ /** @deprecated Use {@link Account}. */
1165
+ set account(value) {
1166
+ this.Account = value;
1167
+ }
1168
+ /** @deprecated Use {@link Account}. */
1169
+ get account() {
1170
+ return this.Account;
1171
+ }
1164
1172
  /**
1165
1173
  * The current folder path to display
1166
1174
  */
1167
- folderPath = '';
1175
+ FolderPath = '';
1176
+ /** @deprecated Use {@link FolderPath}. */
1177
+ set folderPath(value) {
1178
+ this.FolderPath = value;
1179
+ }
1180
+ /** @deprecated Use {@link FolderPath}. */
1181
+ get folderPath() {
1182
+ return this.FolderPath;
1183
+ }
1168
1184
  /**
1169
1185
  * Emits when a folder is double-clicked for navigation
1170
1186
  */
1171
- folderNavigate = new EventEmitter();
1187
+ FolderNavigate = new EventEmitter();
1188
+ /**
1189
+ * @deprecated Use {@link FolderNavigate}.
1190
+ *
1191
+ * The same emitter under the old binding name, so a template still binding
1192
+ * (folderNavigate) keeps working. Must stay AFTER FolderNavigate: class fields
1193
+ * initialise in order, and the other way round this captures undefined.
1194
+ */
1195
+ folderNavigate = this.FolderNavigate;
1172
1196
  /**
1173
1197
  * Emits when the folder structure has changed (e.g., new folder created)
1174
1198
  * This signals that the folder tree should refresh
1175
1199
  */
1176
- folderStructureChanged = new EventEmitter();
1200
+ FolderStructureChanged = new EventEmitter();
1201
+ /**
1202
+ * @deprecated Use {@link FolderStructureChanged}.
1203
+ *
1204
+ * The same emitter under the old binding name, so a template still binding
1205
+ * (folderStructureChanged) keeps working. Must stay AFTER FolderStructureChanged: class fields
1206
+ * initialise in order, and the other way round this captures undefined.
1207
+ */
1208
+ folderStructureChanged = this.FolderStructureChanged;
1177
1209
  /**
1178
1210
  * List of files and folders in the current directory
1179
1211
  */
1180
- items = [];
1212
+ Items = [];
1213
+ /** @deprecated Use {@link Items}. */
1214
+ get items() {
1215
+ return this.Items;
1216
+ }
1217
+ /** @deprecated Use {@link Items}. */
1218
+ set items(value) {
1219
+ this.Items = value;
1220
+ }
1181
1221
  /**
1182
1222
  * Currently selected item keys in the grid (Kendo stores keys, not full objects)
1183
1223
  */
1184
- selectedItems = [];
1224
+ SelectedItems = [];
1225
+ /** @deprecated Use {@link SelectedItems}. */
1226
+ get selectedItems() {
1227
+ return this.SelectedItems;
1228
+ }
1229
+ /** @deprecated Use {@link SelectedItems}. */
1230
+ set selectedItems(value) {
1231
+ this.SelectedItems = value;
1232
+ }
1185
1233
  cdr = inject(ChangeDetectorRef);
1186
1234
  /**
1187
1235
  * Loading state indicator
@@ -1194,94 +1242,438 @@ export class FileGridComponent {
1194
1242
  /**
1195
1243
  * View mode: 'grid' or 'list'
1196
1244
  */
1197
- viewMode = 'list';
1245
+ ViewMode = 'list';
1246
+ /** @deprecated Use {@link ViewMode}. */
1247
+ get viewMode() {
1248
+ return this.ViewMode;
1249
+ }
1250
+ /** @deprecated Use {@link ViewMode}. */
1251
+ set viewMode(value) {
1252
+ this.ViewMode = value;
1253
+ }
1198
1254
  /**
1199
1255
  * Sort configuration for the grid
1200
1256
  */
1201
- sort = [
1257
+ Sort = [
1202
1258
  { field: 'name', dir: 'asc' }
1203
1259
  ];
1260
+ /** @deprecated Use {@link Sort}. */
1261
+ get sort() {
1262
+ return this.Sort;
1263
+ }
1264
+ /** @deprecated Use {@link Sort}. */
1265
+ set sort(value) {
1266
+ this.Sort = value;
1267
+ }
1204
1268
  /**
1205
1269
  * Search query for filtering files/folders
1206
1270
  */
1207
- searchQuery = '';
1271
+ SearchQuery = '';
1272
+ /** @deprecated Use {@link SearchQuery}. */
1273
+ get searchQuery() {
1274
+ return this.SearchQuery;
1275
+ }
1276
+ /** @deprecated Use {@link SearchQuery}. */
1277
+ set searchQuery(value) {
1278
+ this.SearchQuery = value;
1279
+ }
1208
1280
  /**
1209
1281
  * File type filter ('all', 'files', 'folders')
1210
1282
  */
1211
- fileTypeFilter = 'all';
1283
+ FileTypeFilter = 'all';
1284
+ /** @deprecated Use {@link FileTypeFilter}. */
1285
+ get fileTypeFilter() {
1286
+ return this.FileTypeFilter;
1287
+ }
1288
+ /** @deprecated Use {@link FileTypeFilter}. */
1289
+ set fileTypeFilter(value) {
1290
+ this.FileTypeFilter = value;
1291
+ }
1212
1292
  /**
1213
1293
  * Filtered list of items based on search and filter criteria
1214
1294
  */
1215
- filteredItems = [];
1295
+ FilteredItems = [];
1296
+ /** @deprecated Use {@link FilteredItems}. */
1297
+ get filteredItems() {
1298
+ return this.FilteredItems;
1299
+ }
1300
+ /** @deprecated Use {@link FilteredItems}. */
1301
+ set filteredItems(value) {
1302
+ this.FilteredItems = value;
1303
+ }
1216
1304
  /**
1217
1305
  * Drag-and-drop state
1218
1306
  */
1219
- isDragging = false;
1307
+ IsDragging = false;
1308
+ /** @deprecated Use {@link IsDragging}. */
1309
+ get isDragging() {
1310
+ return this.IsDragging;
1311
+ }
1312
+ /** @deprecated Use {@link IsDragging}. */
1313
+ set isDragging(value) {
1314
+ this.IsDragging = value;
1315
+ }
1220
1316
  /**
1221
1317
  * Upload progress state
1222
1318
  */
1223
- isUploading = false;
1224
- uploadProgress = 0;
1225
- uploadingFileName = '';
1319
+ IsUploading = false;
1320
+ /** @deprecated Use {@link IsUploading}. */
1321
+ get isUploading() {
1322
+ return this.IsUploading;
1323
+ }
1324
+ /** @deprecated Use {@link IsUploading}. */
1325
+ set isUploading(value) {
1326
+ this.IsUploading = value;
1327
+ }
1328
+ UploadProgress = 0;
1329
+ /** @deprecated Use {@link UploadProgress}. */
1330
+ get uploadProgress() {
1331
+ return this.UploadProgress;
1332
+ }
1333
+ /** @deprecated Use {@link UploadProgress}. */
1334
+ set uploadProgress(value) {
1335
+ this.UploadProgress = value;
1336
+ }
1337
+ UploadingFileName = '';
1338
+ /** @deprecated Use {@link UploadingFileName}. */
1339
+ get uploadingFileName() {
1340
+ return this.UploadingFileName;
1341
+ }
1342
+ /** @deprecated Use {@link UploadingFileName}. */
1343
+ set uploadingFileName(value) {
1344
+ this.UploadingFileName = value;
1345
+ }
1226
1346
  /**
1227
1347
  * New folder dialog state
1228
1348
  */
1229
- showNewFolderDialog = false;
1230
- newFolderName = '';
1231
- isCreatingFolder = false;
1349
+ ShowNewFolderDialog = false;
1350
+ /** @deprecated Use {@link ShowNewFolderDialog}. */
1351
+ get showNewFolderDialog() {
1352
+ return this.ShowNewFolderDialog;
1353
+ }
1354
+ /** @deprecated Use {@link ShowNewFolderDialog}. */
1355
+ set showNewFolderDialog(value) {
1356
+ this.ShowNewFolderDialog = value;
1357
+ }
1358
+ NewFolderName = '';
1359
+ /** @deprecated Use {@link NewFolderName}. */
1360
+ get newFolderName() {
1361
+ return this.NewFolderName;
1362
+ }
1363
+ /** @deprecated Use {@link NewFolderName}. */
1364
+ set newFolderName(value) {
1365
+ this.NewFolderName = value;
1366
+ }
1367
+ IsCreatingFolder = false;
1368
+ /** @deprecated Use {@link IsCreatingFolder}. */
1369
+ get isCreatingFolder() {
1370
+ return this.IsCreatingFolder;
1371
+ }
1372
+ /** @deprecated Use {@link IsCreatingFolder}. */
1373
+ set isCreatingFolder(value) {
1374
+ this.IsCreatingFolder = value;
1375
+ }
1232
1376
  /**
1233
1377
  * Delete confirmation dialog state
1234
1378
  */
1235
1379
  showDeleteDialog = false;
1236
- itemToDelete = null;
1237
- isDeleting = false;
1380
+ ItemToDelete = null;
1381
+ /** @deprecated Use {@link ItemToDelete}. */
1382
+ get itemToDelete() {
1383
+ return this.ItemToDelete;
1384
+ }
1385
+ /** @deprecated Use {@link ItemToDelete}. */
1386
+ set itemToDelete(value) {
1387
+ this.ItemToDelete = value;
1388
+ }
1389
+ IsDeleting = false;
1390
+ /** @deprecated Use {@link IsDeleting}. */
1391
+ get isDeleting() {
1392
+ return this.IsDeleting;
1393
+ }
1394
+ /** @deprecated Use {@link IsDeleting}. */
1395
+ set isDeleting(value) {
1396
+ this.IsDeleting = value;
1397
+ }
1238
1398
  /**
1239
1399
  * Rename dialog state
1240
1400
  */
1241
- showRenameDialog = false;
1242
- itemToRename = null;
1243
- newItemName = '';
1244
- isRenaming = false;
1401
+ ShowRenameDialog = false;
1402
+ /** @deprecated Use {@link ShowRenameDialog}. */
1403
+ get showRenameDialog() {
1404
+ return this.ShowRenameDialog;
1405
+ }
1406
+ /** @deprecated Use {@link ShowRenameDialog}. */
1407
+ set showRenameDialog(value) {
1408
+ this.ShowRenameDialog = value;
1409
+ }
1410
+ ItemToRename = null;
1411
+ /** @deprecated Use {@link ItemToRename}. */
1412
+ get itemToRename() {
1413
+ return this.ItemToRename;
1414
+ }
1415
+ /** @deprecated Use {@link ItemToRename}. */
1416
+ set itemToRename(value) {
1417
+ this.ItemToRename = value;
1418
+ }
1419
+ NewItemName = '';
1420
+ /** @deprecated Use {@link NewItemName}. */
1421
+ get newItemName() {
1422
+ return this.NewItemName;
1423
+ }
1424
+ /** @deprecated Use {@link NewItemName}. */
1425
+ set newItemName(value) {
1426
+ this.NewItemName = value;
1427
+ }
1428
+ IsRenaming = false;
1429
+ /** @deprecated Use {@link IsRenaming}. */
1430
+ get isRenaming() {
1431
+ return this.IsRenaming;
1432
+ }
1433
+ /** @deprecated Use {@link IsRenaming}. */
1434
+ set isRenaming(value) {
1435
+ this.IsRenaming = value;
1436
+ }
1245
1437
  /**
1246
1438
  * Copy dialog state
1247
1439
  */
1248
- showCopyDialog = false;
1249
- itemToCopy = null;
1250
- copyDestinationPath = '';
1251
- isCopying = false;
1440
+ ShowCopyDialog = false;
1441
+ /** @deprecated Use {@link ShowCopyDialog}. */
1442
+ get showCopyDialog() {
1443
+ return this.ShowCopyDialog;
1444
+ }
1445
+ /** @deprecated Use {@link ShowCopyDialog}. */
1446
+ set showCopyDialog(value) {
1447
+ this.ShowCopyDialog = value;
1448
+ }
1449
+ ItemToCopy = null;
1450
+ /** @deprecated Use {@link ItemToCopy}. */
1451
+ get itemToCopy() {
1452
+ return this.ItemToCopy;
1453
+ }
1454
+ /** @deprecated Use {@link ItemToCopy}. */
1455
+ set itemToCopy(value) {
1456
+ this.ItemToCopy = value;
1457
+ }
1458
+ CopyDestinationPath = '';
1459
+ /** @deprecated Use {@link CopyDestinationPath}. */
1460
+ get copyDestinationPath() {
1461
+ return this.CopyDestinationPath;
1462
+ }
1463
+ /** @deprecated Use {@link CopyDestinationPath}. */
1464
+ set copyDestinationPath(value) {
1465
+ this.CopyDestinationPath = value;
1466
+ }
1467
+ IsCopying = false;
1468
+ /** @deprecated Use {@link IsCopying}. */
1469
+ get isCopying() {
1470
+ return this.IsCopying;
1471
+ }
1472
+ /** @deprecated Use {@link IsCopying}. */
1473
+ set isCopying(value) {
1474
+ this.IsCopying = value;
1475
+ }
1252
1476
  /**
1253
1477
  * Move dialog state
1254
1478
  */
1255
- showMoveDialog = false;
1256
- itemToMove = null;
1257
- moveDestinationPath = '';
1258
- isMoving = false;
1479
+ ShowMoveDialog = false;
1480
+ /** @deprecated Use {@link ShowMoveDialog}. */
1481
+ get showMoveDialog() {
1482
+ return this.ShowMoveDialog;
1483
+ }
1484
+ /** @deprecated Use {@link ShowMoveDialog}. */
1485
+ set showMoveDialog(value) {
1486
+ this.ShowMoveDialog = value;
1487
+ }
1488
+ ItemToMove = null;
1489
+ /** @deprecated Use {@link ItemToMove}. */
1490
+ get itemToMove() {
1491
+ return this.ItemToMove;
1492
+ }
1493
+ /** @deprecated Use {@link ItemToMove}. */
1494
+ set itemToMove(value) {
1495
+ this.ItemToMove = value;
1496
+ }
1497
+ MoveDestinationPath = '';
1498
+ /** @deprecated Use {@link MoveDestinationPath}. */
1499
+ get moveDestinationPath() {
1500
+ return this.MoveDestinationPath;
1501
+ }
1502
+ /** @deprecated Use {@link MoveDestinationPath}. */
1503
+ set moveDestinationPath(value) {
1504
+ this.MoveDestinationPath = value;
1505
+ }
1506
+ IsMoving = false;
1507
+ /** @deprecated Use {@link IsMoving}. */
1508
+ get isMoving() {
1509
+ return this.IsMoving;
1510
+ }
1511
+ /** @deprecated Use {@link IsMoving}. */
1512
+ set isMoving(value) {
1513
+ this.IsMoving = value;
1514
+ }
1259
1515
  /**
1260
1516
  * Copy to provider dialog state
1261
1517
  */
1262
- showCopyToProviderDialog = false;
1263
- itemToCopyToProvider = null;
1264
- availableAccounts = [];
1265
- selectedDestinationAccounts = new Set();
1266
- copyToAccountDestinationPath = '';
1267
- isCopyingToAccount = false;
1268
- copyToAccountProgress = null;
1518
+ ShowCopyToProviderDialog = false;
1519
+ /** @deprecated Use {@link ShowCopyToProviderDialog}. */
1520
+ get showCopyToProviderDialog() {
1521
+ return this.ShowCopyToProviderDialog;
1522
+ }
1523
+ /** @deprecated Use {@link ShowCopyToProviderDialog}. */
1524
+ set showCopyToProviderDialog(value) {
1525
+ this.ShowCopyToProviderDialog = value;
1526
+ }
1527
+ ItemToCopyToProvider = null;
1528
+ /** @deprecated Use {@link ItemToCopyToProvider}. */
1529
+ get itemToCopyToProvider() {
1530
+ return this.ItemToCopyToProvider;
1531
+ }
1532
+ /** @deprecated Use {@link ItemToCopyToProvider}. */
1533
+ set itemToCopyToProvider(value) {
1534
+ this.ItemToCopyToProvider = value;
1535
+ }
1536
+ AvailableAccounts = [];
1537
+ /** @deprecated Use {@link AvailableAccounts}. */
1538
+ get availableAccounts() {
1539
+ return this.AvailableAccounts;
1540
+ }
1541
+ /** @deprecated Use {@link AvailableAccounts}. */
1542
+ set availableAccounts(value) {
1543
+ this.AvailableAccounts = value;
1544
+ }
1545
+ SelectedDestinationAccounts = new Set();
1546
+ /** @deprecated Use {@link SelectedDestinationAccounts}. */
1547
+ get selectedDestinationAccounts() {
1548
+ return this.SelectedDestinationAccounts;
1549
+ }
1550
+ /** @deprecated Use {@link SelectedDestinationAccounts}. */
1551
+ set selectedDestinationAccounts(value) {
1552
+ this.SelectedDestinationAccounts = value;
1553
+ }
1554
+ CopyToAccountDestinationPath = '';
1555
+ /** @deprecated Use {@link CopyToAccountDestinationPath}. */
1556
+ get copyToAccountDestinationPath() {
1557
+ return this.CopyToAccountDestinationPath;
1558
+ }
1559
+ /** @deprecated Use {@link CopyToAccountDestinationPath}. */
1560
+ set copyToAccountDestinationPath(value) {
1561
+ this.CopyToAccountDestinationPath = value;
1562
+ }
1563
+ IsCopyingToAccount = false;
1564
+ /** @deprecated Use {@link IsCopyingToAccount}. */
1565
+ get isCopyingToAccount() {
1566
+ return this.IsCopyingToAccount;
1567
+ }
1568
+ /** @deprecated Use {@link IsCopyingToAccount}. */
1569
+ set isCopyingToAccount(value) {
1570
+ this.IsCopyingToAccount = value;
1571
+ }
1572
+ CopyToAccountProgress = null;
1573
+ /** @deprecated Use {@link CopyToAccountProgress}. */
1574
+ get copyToAccountProgress() {
1575
+ return this.CopyToAccountProgress;
1576
+ }
1577
+ /** @deprecated Use {@link CopyToAccountProgress}. */
1578
+ set copyToAccountProgress(value) {
1579
+ this.CopyToAccountProgress = value;
1580
+ }
1269
1581
  /**
1270
1582
  * Multi-provider search state
1271
1583
  */
1272
- isMultiProviderSearchMode = false;
1273
- multiProviderSearchQuery = '';
1274
- selectedSearchProviders = new Set();
1275
- isSearching = false;
1276
- multiProviderSearchResults = null;
1584
+ IsMultiProviderSearchMode = false;
1585
+ /** @deprecated Use {@link IsMultiProviderSearchMode}. */
1586
+ get isMultiProviderSearchMode() {
1587
+ return this.IsMultiProviderSearchMode;
1588
+ }
1589
+ /** @deprecated Use {@link IsMultiProviderSearchMode}. */
1590
+ set isMultiProviderSearchMode(value) {
1591
+ this.IsMultiProviderSearchMode = value;
1592
+ }
1593
+ MultiProviderSearchQuery = '';
1594
+ /** @deprecated Use {@link MultiProviderSearchQuery}. */
1595
+ get multiProviderSearchQuery() {
1596
+ return this.MultiProviderSearchQuery;
1597
+ }
1598
+ /** @deprecated Use {@link MultiProviderSearchQuery}. */
1599
+ set multiProviderSearchQuery(value) {
1600
+ this.MultiProviderSearchQuery = value;
1601
+ }
1602
+ SelectedSearchProviders = new Set();
1603
+ /** @deprecated Use {@link SelectedSearchProviders}. */
1604
+ get selectedSearchProviders() {
1605
+ return this.SelectedSearchProviders;
1606
+ }
1607
+ /** @deprecated Use {@link SelectedSearchProviders}. */
1608
+ set selectedSearchProviders(value) {
1609
+ this.SelectedSearchProviders = value;
1610
+ }
1611
+ IsSearching = false;
1612
+ /** @deprecated Use {@link IsSearching}. */
1613
+ get isSearching() {
1614
+ return this.IsSearching;
1615
+ }
1616
+ /** @deprecated Use {@link IsSearching}. */
1617
+ set isSearching(value) {
1618
+ this.IsSearching = value;
1619
+ }
1620
+ MultiProviderSearchResults = null;
1621
+ /** @deprecated Use {@link MultiProviderSearchResults}. */
1622
+ get multiProviderSearchResults() {
1623
+ return this.MultiProviderSearchResults;
1624
+ }
1625
+ /** @deprecated Use {@link MultiProviderSearchResults}. */
1626
+ set multiProviderSearchResults(value) {
1627
+ this.MultiProviderSearchResults = value;
1628
+ }
1277
1629
  /**
1278
1630
  * Preview modal state
1279
1631
  */
1280
- showPreviewModal = false;
1281
- previewItem = null;
1282
- previewUrl = null;
1283
- isLoadingPreview = false;
1284
- previewMediaType = 'unknown';
1632
+ ShowPreviewModal = false;
1633
+ /** @deprecated Use {@link ShowPreviewModal}. */
1634
+ get showPreviewModal() {
1635
+ return this.ShowPreviewModal;
1636
+ }
1637
+ /** @deprecated Use {@link ShowPreviewModal}. */
1638
+ set showPreviewModal(value) {
1639
+ this.ShowPreviewModal = value;
1640
+ }
1641
+ PreviewItem = null;
1642
+ /** @deprecated Use {@link PreviewItem}. */
1643
+ get previewItem() {
1644
+ return this.PreviewItem;
1645
+ }
1646
+ /** @deprecated Use {@link PreviewItem}. */
1647
+ set previewItem(value) {
1648
+ this.PreviewItem = value;
1649
+ }
1650
+ PreviewUrl = null;
1651
+ /** @deprecated Use {@link PreviewUrl}. */
1652
+ get previewUrl() {
1653
+ return this.PreviewUrl;
1654
+ }
1655
+ /** @deprecated Use {@link PreviewUrl}. */
1656
+ set previewUrl(value) {
1657
+ this.PreviewUrl = value;
1658
+ }
1659
+ IsLoadingPreview = false;
1660
+ /** @deprecated Use {@link IsLoadingPreview}. */
1661
+ get isLoadingPreview() {
1662
+ return this.IsLoadingPreview;
1663
+ }
1664
+ /** @deprecated Use {@link IsLoadingPreview}. */
1665
+ set isLoadingPreview(value) {
1666
+ this.IsLoadingPreview = value;
1667
+ }
1668
+ PreviewMediaType = 'unknown';
1669
+ /** @deprecated Use {@link PreviewMediaType}. */
1670
+ get previewMediaType() {
1671
+ return this.PreviewMediaType;
1672
+ }
1673
+ /** @deprecated Use {@link PreviewMediaType}. */
1674
+ set previewMediaType(value) {
1675
+ this.PreviewMediaType = value;
1676
+ }
1285
1677
  /**
1286
1678
  * GraphQL client for file storage operations
1287
1679
  */
@@ -1306,21 +1698,21 @@ export class FileGridComponent {
1306
1698
  * Loads files and folders from the current path
1307
1699
  */
1308
1700
  async loadItems() {
1309
- if (!this.account) {
1310
- this.items = [];
1701
+ if (!this.Account) {
1702
+ this.Items = [];
1311
1703
  return;
1312
1704
  }
1313
- const previousItemCount = this.items.length;
1314
- const previousItemNames = this.items.map(i => i.name);
1705
+ const previousItemCount = this.Items.length;
1706
+ const previousItemNames = this.Items.map(i => i.name);
1315
1707
  this.isLoading = true;
1316
1708
  this.cdr.detectChanges();
1317
1709
  this.errorMessage = null;
1318
1710
  try {
1319
- console.log('[FileGrid] Loading items for account:', this.account.account.ID, 'path:', this.folderPath);
1711
+ console.log('[FileGrid] Loading items for account:', this.Account.account.ID, 'path:', this.FolderPath);
1320
1712
  console.log('[FileGrid] Previous items:', { count: previousItemCount, names: previousItemNames });
1321
- const listResult = await this.storageClient.ListObjects(this.account.account.ID, this.folderPath || '', '/');
1713
+ const listResult = await this.storageClient.ListObjects(this.Account.account.ID, this.FolderPath || '', '/');
1322
1714
  console.log('[FileGrid] ListObjects result:', listResult);
1323
- this.items = [];
1715
+ this.Items = [];
1324
1716
  // Add folders from prefixes first (so they appear before files)
1325
1717
  if (listResult.prefixes) {
1326
1718
  for (const prefix of listResult.prefixes) {
@@ -1330,13 +1722,13 @@ export class FileGridComponent {
1330
1722
  ? prefix.slice(0, -1).split('/').pop()
1331
1723
  : prefix.split('/').pop();
1332
1724
  if (folderName) {
1333
- this.items.push({
1334
- key: prefix,
1725
+ this.Items.push({
1726
+ Key: prefix,
1335
1727
  name: folderName,
1336
- type: 'folder',
1337
- size: 0,
1338
- lastModified: new Date(),
1339
- contentType: 'application/x-directory'
1728
+ Type: 'folder',
1729
+ Size: 0,
1730
+ LastModified: new Date(),
1731
+ ContentType: 'application/x-directory'
1340
1732
  });
1341
1733
  }
1342
1734
  }
@@ -1348,14 +1740,14 @@ export class FileGridComponent {
1348
1740
  if (obj.isDirectory) {
1349
1741
  continue;
1350
1742
  }
1351
- this.items.push({
1352
- key: obj.fullPath,
1743
+ this.Items.push({
1744
+ Key: obj.fullPath,
1353
1745
  name: obj.name,
1354
- type: 'file',
1355
- size: obj.size,
1356
- lastModified: obj.lastModified,
1357
- contentType: obj.contentType,
1358
- etag: obj.etag
1746
+ Type: 'file',
1747
+ Size: obj.size,
1748
+ LastModified: obj.lastModified,
1749
+ ContentType: obj.contentType,
1750
+ Etag: obj.etag
1359
1751
  });
1360
1752
  }
1361
1753
  }
@@ -1375,46 +1767,58 @@ export class FileGridComponent {
1375
1767
  * Applies search and filter criteria to the items list
1376
1768
  */
1377
1769
  applyFilters() {
1378
- let filtered = [...this.items];
1770
+ let filtered = [...this.Items];
1379
1771
  // Apply file type filter
1380
- if (this.fileTypeFilter === 'files') {
1381
- filtered = filtered.filter(item => item.type === 'file');
1772
+ if (this.FileTypeFilter === 'files') {
1773
+ filtered = filtered.filter(item => item.Type === 'file');
1382
1774
  }
1383
- else if (this.fileTypeFilter === 'folders') {
1384
- filtered = filtered.filter(item => item.type === 'folder');
1775
+ else if (this.FileTypeFilter === 'folders') {
1776
+ filtered = filtered.filter(item => item.Type === 'folder');
1385
1777
  }
1386
1778
  // Apply search query
1387
- if (this.searchQuery.trim()) {
1388
- const query = this.searchQuery.toLowerCase();
1779
+ if (this.SearchQuery.trim()) {
1780
+ const query = this.SearchQuery.toLowerCase();
1389
1781
  filtered = filtered.filter(item => item.name.toLowerCase().includes(query));
1390
1782
  }
1391
- this.filteredItems = filtered;
1783
+ this.FilteredItems = filtered;
1392
1784
  }
1393
1785
  /**
1394
1786
  * Handles search query changes
1395
1787
  */
1396
- onSearchChange(query) {
1397
- this.searchQuery = query;
1788
+ OnSearchChange(query) {
1789
+ this.SearchQuery = query;
1398
1790
  this.applyFilters();
1399
1791
  }
1792
+ /** @deprecated Use {@link OnSearchChange}. */
1793
+ onSearchChange(query) {
1794
+ return this.OnSearchChange(query);
1795
+ }
1400
1796
  /**
1401
1797
  * Clears the search query
1402
1798
  */
1403
- clearSearch() {
1404
- this.searchQuery = '';
1799
+ ClearSearch() {
1800
+ this.SearchQuery = '';
1405
1801
  this.applyFilters();
1406
1802
  }
1803
+ /** @deprecated Use {@link ClearSearch}. */
1804
+ clearSearch() {
1805
+ return this.ClearSearch();
1806
+ }
1407
1807
  /**
1408
1808
  * Handles file type filter changes
1409
1809
  */
1410
- onFileTypeFilterChange(filterType) {
1411
- this.fileTypeFilter = filterType;
1810
+ OnFileTypeFilterChange(filterType) {
1811
+ this.FileTypeFilter = filterType;
1412
1812
  this.applyFilters();
1413
1813
  }
1814
+ /** @deprecated Use {@link OnFileTypeFilterChange}. */
1815
+ onFileTypeFilterChange(filterType) {
1816
+ return this.OnFileTypeFilterChange(filterType);
1817
+ }
1414
1818
  /**
1415
1819
  * Formats a file size in bytes to a human-readable string
1416
1820
  */
1417
- formatFileSize(bytes) {
1821
+ FormatFileSize(bytes) {
1418
1822
  if (bytes === 0)
1419
1823
  return '0 B';
1420
1824
  const k = 1024;
@@ -1422,6 +1826,10 @@ export class FileGridComponent {
1422
1826
  const i = Math.floor(Math.log(bytes) / Math.log(k));
1423
1827
  return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + ' ' + sizes[i];
1424
1828
  }
1829
+ /** @deprecated Use {@link FormatFileSize}. */
1830
+ formatFileSize(bytes) {
1831
+ return this.FormatFileSize(bytes);
1832
+ }
1425
1833
  /**
1426
1834
  * Formats a date to a readable string
1427
1835
  */
@@ -1431,11 +1839,11 @@ export class FileGridComponent {
1431
1839
  /**
1432
1840
  * Gets the icon class for a file or folder
1433
1841
  */
1434
- getItemIcon(item) {
1842
+ GetItemIcon(item) {
1435
1843
  if (!item || !item.name) {
1436
1844
  return 'fa-solid fa-file';
1437
1845
  }
1438
- if (item.type === 'folder') {
1846
+ if (item.Type === 'folder') {
1439
1847
  return 'fa-solid fa-folder';
1440
1848
  }
1441
1849
  // Determine file icon based on content type or extension
@@ -1474,68 +1882,84 @@ export class FileGridComponent {
1474
1882
  return 'fa-solid fa-file';
1475
1883
  }
1476
1884
  }
1885
+ /** @deprecated Use {@link GetItemIcon}. */
1886
+ getItemIcon(item) {
1887
+ return this.GetItemIcon(item);
1888
+ }
1477
1889
  /**
1478
1890
  * Handles item selection change (not used - Kendo handles selection internally)
1479
1891
  */
1892
+ OnSelectionChange(selectedKeys) {
1893
+ this.SelectedItems = selectedKeys;
1894
+ }
1895
+ /** @deprecated Use {@link OnSelectionChange}. */
1480
1896
  onSelectionChange(selectedKeys) {
1481
- this.selectedItems = selectedKeys;
1897
+ return this.OnSelectionChange(selectedKeys);
1482
1898
  }
1483
1899
  /**
1484
1900
  * Handles tile click in grid view for selection
1485
1901
  */
1486
- onTileClick(item, event) {
1902
+ OnTileClick(item, event) {
1487
1903
  if (event.ctrlKey || event.metaKey) {
1488
1904
  // Multi-select: toggle item key
1489
- const index = this.selectedItems.indexOf(item.key);
1905
+ const index = this.SelectedItems.indexOf(item.Key);
1490
1906
  if (index >= 0) {
1491
- this.selectedItems.splice(index, 1);
1907
+ this.SelectedItems.splice(index, 1);
1492
1908
  }
1493
1909
  else {
1494
- this.selectedItems.push(item.key);
1910
+ this.SelectedItems.push(item.Key);
1495
1911
  }
1496
1912
  }
1497
- else if (event.shiftKey && this.selectedItems.length > 0) {
1913
+ else if (event.shiftKey && this.SelectedItems.length > 0) {
1498
1914
  // Range select: select from last selected to current
1499
- const lastSelectedKey = this.selectedItems[this.selectedItems.length - 1];
1500
- const lastSelected = this.items.find(i => i.key === lastSelectedKey);
1915
+ const lastSelectedKey = this.SelectedItems[this.SelectedItems.length - 1];
1916
+ const lastSelected = this.Items.find(i => i.Key === lastSelectedKey);
1501
1917
  if (lastSelected) {
1502
- const lastIndex = this.items.indexOf(lastSelected);
1503
- const currentIndex = this.items.indexOf(item);
1918
+ const lastIndex = this.Items.indexOf(lastSelected);
1919
+ const currentIndex = this.Items.indexOf(item);
1504
1920
  const start = Math.min(lastIndex, currentIndex);
1505
1921
  const end = Math.max(lastIndex, currentIndex);
1506
- this.selectedItems = this.items.slice(start, end + 1).map(i => i.key);
1922
+ this.SelectedItems = this.Items.slice(start, end + 1).map(i => i.Key);
1507
1923
  }
1508
1924
  }
1509
1925
  else {
1510
1926
  // Single select: replace selection
1511
- this.selectedItems = [item.key];
1927
+ this.SelectedItems = [item.Key];
1512
1928
  }
1513
1929
  }
1930
+ /** @deprecated Use {@link OnTileClick}. */
1931
+ onTileClick(item, event) {
1932
+ return this.OnTileClick(item, event);
1933
+ }
1514
1934
  /**
1515
1935
  * Handles double-click on an item
1516
1936
  * For folders, navigate into them. For files, open in-browser preview.
1517
1937
  */
1518
- onItemDoubleClick(item) {
1519
- if (item.type === 'folder') {
1938
+ OnItemDoubleClick(item) {
1939
+ if (item.Type === 'folder') {
1520
1940
  // Navigate into folder by emitting the folder path
1521
- console.log('[FileGrid] Navigating to folder:', item.key);
1522
- this.folderNavigate.emit(item.key);
1941
+ console.log('[FileGrid] Navigating to folder:', item.Key);
1942
+ this.FolderNavigate.emit(item.Key);
1523
1943
  }
1524
1944
  else {
1525
1945
  // Open in-browser preview
1526
- this.openPreview(item);
1946
+ this.OpenPreview(item);
1527
1947
  }
1528
1948
  }
1949
+ /** @deprecated Use {@link OnItemDoubleClick}. */
1950
+ onItemDoubleClick(item) {
1951
+ return this.OnItemDoubleClick(item);
1952
+ }
1529
1953
  /**
1530
1954
  * Downloads a file by creating a pre-authenticated download URL
1531
1955
  */
1532
- async downloadFile(item) {
1533
- if (!this.account) {
1956
+ async DownloadFile(item) {
1957
+ if (!this.Account) {
1534
1958
  return;
1535
1959
  }
1536
1960
  try {
1537
- console.log('[FileGrid] Downloading file:', item.key);
1538
- const downloadUrl = await this.storageClient.CreatePreAuthDownloadUrl(this.account.account.ID, item.key);
1961
+ console.log('[FileGrid] Downloading file:', item.Key);
1962
+ const downloadUrl = await this.storageClient.CreatePreAuthDownloadUrl(this.Account.account.ID, item.Key);
1539
1963
  console.log('[FileGrid] Download URL created:', downloadUrl ? 'success' : 'failed');
1540
1964
  if (downloadUrl) {
1541
1965
  // Open file in new browser tab for viewing/download
@@ -1552,54 +1976,78 @@ export class FileGridComponent {
1552
1976
  this.notifications.CreateSimpleNotification(`Failed to download ${item.name}: ${errorMessage}`, 'error');
1553
1977
  }
1554
1978
  }
1979
+ /** @deprecated Use {@link DownloadFile}. */
1980
+ async downloadFile(item) {
1981
+ return this.DownloadFile(item);
1982
+ }
1555
1983
  /**
1556
1984
  * Toggles between grid and list view
1557
1985
  */
1986
+ ToggleViewMode() {
1987
+ this.ViewMode = this.ViewMode === 'grid' ? 'list' : 'grid';
1988
+ }
1989
+ /** @deprecated Use {@link ToggleViewMode}. */
1558
1990
  toggleViewMode() {
1559
- this.viewMode = this.viewMode === 'grid' ? 'list' : 'grid';
1991
+ return this.ToggleViewMode();
1560
1992
  }
1561
1993
  /**
1562
1994
  * Refreshes the current directory
1563
1995
  */
1564
- refresh() {
1996
+ Refresh() {
1565
1997
  this.loadItems();
1566
1998
  }
1999
+ /** @deprecated Use {@link Refresh}. */
2000
+ refresh() {
2001
+ return this.Refresh();
2002
+ }
1567
2003
  /**
1568
2004
  * Navigates up to the parent directory
1569
2005
  */
1570
- navigateUp() {
1571
- if (!this.folderPath) {
2006
+ NavigateUp() {
2007
+ if (!this.FolderPath) {
1572
2008
  // Already at root
1573
2009
  return;
1574
2010
  }
1575
2011
  // Remove trailing slash if present
1576
- const cleanPath = this.folderPath.endsWith('/')
1577
- ? this.folderPath.slice(0, -1)
1578
- : this.folderPath;
2012
+ const cleanPath = this.FolderPath.endsWith('/')
2013
+ ? this.FolderPath.slice(0, -1)
2014
+ : this.FolderPath;
1579
2015
  // Get parent path by removing last segment
1580
2016
  const segments = cleanPath.split('/').filter(s => s.length > 0);
1581
2017
  segments.pop(); // Remove last segment
1582
2018
  const parentPath = segments.length > 0 ? segments.join('/') + '/' : '';
1583
2019
  // Emit navigation event to update the folder tree and path
1584
- this.folderNavigate.emit(parentPath);
2020
+ this.FolderNavigate.emit(parentPath);
2021
+ }
2022
+ /** @deprecated Use {@link NavigateUp}. */
2023
+ navigateUp() {
2024
+ return this.NavigateUp();
1585
2025
  }
1586
2026
  /**
1587
2027
  * Checks if we can navigate up (not at root)
1588
2028
  */
2029
+ CanNavigateUp() {
2030
+ return this.FolderPath !== '' && this.FolderPath !== '/';
2031
+ }
2032
+ /** @deprecated Use {@link CanNavigateUp}. */
1589
2033
  canNavigateUp() {
1590
- return this.folderPath !== '' && this.folderPath !== '/';
2034
+ return this.CanNavigateUp();
1591
2035
  }
1592
2036
  /**
1593
2037
  * Handles sort change event from the grid
1594
2038
  */
2039
+ OnSortChange(sort) {
2040
+ this.Sort = sort;
2041
+ }
2042
+ /** @deprecated Use {@link OnSortChange}. */
1595
2043
  onSortChange(sort) {
1596
- this.sort = sort;
2044
+ return this.OnSortChange(sort);
1597
2045
  }
1598
2046
  /**
1599
2047
  * Gets a human-readable file type based on extension
1600
2048
  */
1601
- getFileType(item) {
1602
- if (item.type === 'folder') {
2049
+ GetFileType(item) {
2050
+ if (item.Type === 'folder') {
1603
2051
  return 'Folder';
1604
2052
  }
1605
2053
  const extension = item.name.split('.').pop()?.toLowerCase() || '';
@@ -1645,10 +2093,14 @@ export class FileGridComponent {
1645
2093
  };
1646
2094
  return typeMap[extension] || `${extension.toUpperCase()} File`;
1647
2095
  }
2096
+ /** @deprecated Use {@link GetFileType}. */
2097
+ getFileType(item) {
2098
+ return this.GetFileType(item);
2099
+ }
1648
2100
  /**
1649
2101
  * Handles upload button click - triggers file input
1650
2102
  */
1651
- onUploadClick() {
2103
+ OnUploadClick() {
1652
2104
  const fileInput = document.createElement('input');
1653
2105
  fileInput.type = 'file';
1654
2106
  fileInput.multiple = true;
@@ -1660,49 +2112,69 @@ export class FileGridComponent {
1660
2112
  };
1661
2113
  fileInput.click();
1662
2114
  }
2115
+ /** @deprecated Use {@link OnUploadClick}. */
2116
+ onUploadClick() {
2117
+ return this.OnUploadClick();
2118
+ }
1663
2119
  /**
1664
2120
  * Handles drag enter event
1665
2121
  */
1666
- onDragEnter(event) {
2122
+ OnDragEnter(event) {
1667
2123
  event.preventDefault();
1668
2124
  event.stopPropagation();
1669
- this.isDragging = true;
2125
+ this.IsDragging = true;
2126
+ }
2127
+ /** @deprecated Use {@link OnDragEnter}. */
2128
+ onDragEnter(event) {
2129
+ return this.OnDragEnter(event);
1670
2130
  }
1671
2131
  /**
1672
2132
  * Handles drag over event
1673
2133
  */
1674
- onDragOver(event) {
2134
+ OnDragOver(event) {
1675
2135
  event.preventDefault();
1676
2136
  event.stopPropagation();
1677
2137
  }
2138
+ /** @deprecated Use {@link OnDragOver}. */
2139
+ onDragOver(event) {
2140
+ return this.OnDragOver(event);
2141
+ }
1678
2142
  /**
1679
2143
  * Handles drag leave event
1680
2144
  */
1681
- onDragLeave(event) {
2145
+ OnDragLeave(event) {
1682
2146
  event.preventDefault();
1683
2147
  event.stopPropagation();
1684
2148
  // Only hide if leaving the container itself, not child elements
1685
2149
  if (event.currentTarget === event.target) {
1686
- this.isDragging = false;
2150
+ this.IsDragging = false;
1687
2151
  }
1688
2152
  }
2153
+ /** @deprecated Use {@link OnDragLeave}. */
2154
+ onDragLeave(event) {
2155
+ return this.OnDragLeave(event);
2156
+ }
1689
2157
  /**
1690
2158
  * Handles drop event
1691
2159
  */
1692
- onDrop(event) {
2160
+ OnDrop(event) {
1693
2161
  event.preventDefault();
1694
2162
  event.stopPropagation();
1695
- this.isDragging = false;
2163
+ this.IsDragging = false;
1696
2164
  const files = event.dataTransfer?.files;
1697
2165
  if (files && files.length > 0) {
1698
2166
  this.uploadFiles(Array.from(files));
1699
2167
  }
1700
2168
  }
2169
+ /** @deprecated Use {@link OnDrop}. */
2170
+ onDrop(event) {
2171
+ return this.OnDrop(event);
2172
+ }
1701
2173
  /**
1702
2174
  * Uploads multiple files to the current folder
1703
2175
  */
1704
2176
  async uploadFiles(files) {
1705
- if (!this.account) {
2177
+ if (!this.Account) {
1706
2178
  return;
1707
2179
  }
1708
2180
  console.log('[FileGrid] Uploading files:', files.map(f => f.name));
@@ -1717,16 +2189,16 @@ export class FileGridComponent {
1717
2189
  * Uploads a single file using server-side MJ Storage (supports all providers)
1718
2190
  */
1719
2191
  async uploadSingleFile(file, current, total) {
1720
- if (!this.account) {
2192
+ if (!this.Account) {
1721
2193
  return;
1722
2194
  }
1723
- this.isUploading = true;
1724
- this.uploadingFileName = `${file.name} (${current}/${total})`;
1725
- this.uploadProgress = 0;
2195
+ this.IsUploading = true;
2196
+ this.UploadingFileName = `${file.name} (${current}/${total})`;
2197
+ this.UploadProgress = 0;
1726
2198
  try {
1727
2199
  let cleanPath = '';
1728
- if (this.folderPath && this.folderPath !== '/') {
1729
- cleanPath = this.folderPath.endsWith('/') ? this.folderPath.slice(0, -1) : this.folderPath;
2200
+ if (this.FolderPath && this.FolderPath !== '/') {
2201
+ cleanPath = this.FolderPath.endsWith('/') ? this.FolderPath.slice(0, -1) : this.FolderPath;
1730
2202
  if (cleanPath.startsWith('/')) {
1731
2203
  cleanPath = cleanPath.substring(1);
1732
2204
  }
@@ -1737,13 +2209,13 @@ export class FileGridComponent {
1737
2209
  FileName: file.name,
1738
2210
  Base64Data: base64Data,
1739
2211
  MimeType: file.type || 'application/octet-stream',
1740
- AccountID: this.account.account.ID,
2212
+ AccountID: this.Account.account.ID,
1741
2213
  PathPrefix: cleanPath || undefined,
1742
2214
  });
1743
2215
  if (!result.Success) {
1744
2216
  throw new Error(result.ErrorMessage || 'Upload failed');
1745
2217
  }
1746
- this.uploadProgress = 100;
2218
+ this.UploadProgress = 100;
1747
2219
  console.log('[FileGrid] File uploaded successfully via MJ Storage:', file.name);
1748
2220
  }
1749
2221
  catch (error) {
@@ -1758,9 +2230,9 @@ export class FileGridComponent {
1758
2230
  }, 5000);
1759
2231
  }
1760
2232
  finally {
1761
- this.isUploading = false;
1762
- this.uploadProgress = 0;
1763
- this.uploadingFileName = '';
2233
+ this.IsUploading = false;
2234
+ this.UploadProgress = 0;
2235
+ this.UploadingFileName = '';
1764
2236
  }
1765
2237
  }
1766
2238
  /**
@@ -1791,7 +2263,7 @@ export class FileGridComponent {
1791
2263
  const xhr = new XMLHttpRequest();
1792
2264
  xhr.upload.addEventListener('progress', (event) => {
1793
2265
  if (event.lengthComputable) {
1794
- this.uploadProgress = Math.round((event.loaded / event.total) * 100);
2266
+ this.UploadProgress = Math.round((event.loaded / event.total) * 100);
1795
2267
  }
1796
2268
  });
1797
2269
  xhr.addEventListener('load', () => {
@@ -1826,49 +2298,57 @@ export class FileGridComponent {
1826
2298
  /**
1827
2299
  * Opens the new folder dialog
1828
2300
  */
2301
+ OnNewFolderClick() {
2302
+ this.NewFolderName = '';
2303
+ this.ShowNewFolderDialog = true;
2304
+ }
2305
+ /** @deprecated Use {@link OnNewFolderClick}. */
1829
2306
  onNewFolderClick() {
1830
- this.newFolderName = '';
1831
- this.showNewFolderDialog = true;
2307
+ return this.OnNewFolderClick();
1832
2308
  }
1833
2309
  /**
1834
2310
  * Closes the new folder dialog
1835
2311
  */
2312
+ OnCancelNewFolder() {
2313
+ this.ShowNewFolderDialog = false;
2314
+ this.NewFolderName = '';
2315
+ }
2316
+ /** @deprecated Use {@link OnCancelNewFolder}. */
1836
2317
  onCancelNewFolder() {
1837
- this.showNewFolderDialog = false;
1838
- this.newFolderName = '';
2318
+ return this.OnCancelNewFolder();
1839
2319
  }
1840
2320
  /**
1841
2321
  * Creates a new folder in the current directory
1842
2322
  */
1843
- async onCreateFolder() {
1844
- if (!this.account || !this.newFolderName.trim()) {
2323
+ async OnCreateFolder() {
2324
+ if (!this.Account || !this.NewFolderName.trim()) {
1845
2325
  return;
1846
2326
  }
1847
- this.isCreatingFolder = true;
2327
+ this.IsCreatingFolder = true;
1848
2328
  try {
1849
2329
  // Construct the full folder path
1850
2330
  // Remove trailing slash from folderPath to avoid double slashes
1851
2331
  let folderPath;
1852
- if (this.folderPath) {
1853
- const cleanPath = this.folderPath.endsWith('/')
1854
- ? this.folderPath.slice(0, -1)
1855
- : this.folderPath;
1856
- folderPath = `${cleanPath}/${this.newFolderName.trim()}`;
2332
+ if (this.FolderPath) {
2333
+ const cleanPath = this.FolderPath.endsWith('/')
2334
+ ? this.FolderPath.slice(0, -1)
2335
+ : this.FolderPath;
2336
+ folderPath = `${cleanPath}/${this.NewFolderName.trim()}`;
1857
2337
  }
1858
2338
  else {
1859
- folderPath = this.newFolderName.trim();
2339
+ folderPath = this.NewFolderName.trim();
1860
2340
  }
1861
2341
  console.log('[FileGrid] Creating folder:', folderPath);
1862
- const success = await this.storageClient.CreateDirectory(this.account.account.ID, folderPath);
2342
+ const success = await this.storageClient.CreateDirectory(this.Account.account.ID, folderPath);
1863
2343
  if (success) {
1864
2344
  console.log('[FileGrid] Folder created successfully:', folderPath);
1865
- const createdName = this.newFolderName.trim();
2345
+ const createdName = this.NewFolderName.trim();
1866
2346
  // Close dialog and refresh
1867
- this.showNewFolderDialog = false;
1868
- this.newFolderName = '';
2347
+ this.ShowNewFolderDialog = false;
2348
+ this.NewFolderName = '';
1869
2349
  this.notifications.CreateSimpleNotification(`Created folder "${createdName}"`, 'success');
1870
2350
  // Notify parent that folder structure changed
1871
- this.folderStructureChanged.emit();
2351
+ this.FolderStructureChanged.emit();
1872
2352
  // Refresh the file grid
1873
2353
  this.loadItems();
1874
2354
  }
@@ -1882,63 +2362,75 @@ export class FileGridComponent {
1882
2362
  this.notifications.CreateSimpleNotification(`Failed to create folder: ${errorMessage}`, 'error');
1883
2363
  }
1884
2364
  finally {
1885
- this.isCreatingFolder = false;
2365
+ this.IsCreatingFolder = false;
1886
2366
  }
1887
2367
  }
2368
+ /** @deprecated Use {@link OnCreateFolder}. */
2369
+ async onCreateFolder() {
2370
+ return this.OnCreateFolder();
2371
+ }
1888
2372
  /**
1889
2373
  * Opens the delete confirmation dialog for the selected items
1890
2374
  */
1891
- onDeleteClick() {
1892
- if (this.selectedItems.length === 0) {
2375
+ OnDeleteClick() {
2376
+ if (this.SelectedItems.length === 0) {
1893
2377
  return;
1894
2378
  }
1895
2379
  // selectedItems contains keys (strings), not full objects
1896
2380
  // Find the actual item object from the items array
1897
- const selectedKey = this.selectedItems[0];
1898
- const item = this.items.find(i => i.key === selectedKey);
2381
+ const selectedKey = this.SelectedItems[0];
2382
+ const item = this.Items.find(i => i.Key === selectedKey);
1899
2383
  if (!item) {
1900
2384
  console.error('[FileGrid] Could not find selected item with key:', selectedKey);
1901
2385
  return;
1902
2386
  }
1903
- this.itemToDelete = item;
2387
+ this.ItemToDelete = item;
1904
2388
  this.showDeleteDialog = true;
1905
2389
  }
2390
+ /** @deprecated Use {@link OnDeleteClick}. */
2391
+ onDeleteClick() {
2392
+ return this.OnDeleteClick();
2393
+ }
1906
2394
  /**
1907
2395
  * Closes the delete confirmation dialog
1908
2396
  */
1909
- onCancelDelete() {
2397
+ OnCancelDelete() {
1910
2398
  this.showDeleteDialog = false;
1911
- this.itemToDelete = null;
2399
+ this.ItemToDelete = null;
2400
+ }
2401
+ /** @deprecated Use {@link OnCancelDelete}. */
2402
+ onCancelDelete() {
2403
+ return this.OnCancelDelete();
1912
2404
  }
1913
2405
  /**
1914
2406
  * Deletes the selected item after confirmation
1915
2407
  */
1916
- async onConfirmDelete() {
1917
- if (!this.account || !this.itemToDelete) {
2408
+ async OnConfirmDelete() {
2409
+ if (!this.Account || !this.ItemToDelete) {
1918
2410
  return;
1919
2411
  }
1920
- this.isDeleting = true;
2412
+ this.IsDeleting = true;
1921
2413
  try {
1922
2414
  // Construct the full path to the item
1923
- const itemPath = this.constructItemPath(this.itemToDelete);
2415
+ const itemPath = this.constructItemPath(this.ItemToDelete);
1924
2416
  console.log('[FileGrid] Deleting item:', itemPath);
1925
- const success = await this.storageClient.DeleteObject(this.account.account.ID, itemPath);
2417
+ const success = await this.storageClient.DeleteObject(this.Account.account.ID, itemPath);
1926
2418
  console.log('[FileGrid] Delete result:', { success });
1927
2419
  if (success) {
1928
2420
  console.log('[FileGrid] Item deleted successfully:', itemPath);
1929
2421
  // Check if we deleted a folder (before clearing itemToDelete)
1930
- const wasFolder = this.itemToDelete.type === 'folder';
1931
- const deletedName = this.itemToDelete.name;
2422
+ const wasFolder = this.ItemToDelete.Type === 'folder';
2423
+ const deletedName = this.ItemToDelete.name;
1932
2424
  // Close dialog
1933
2425
  this.showDeleteDialog = false;
1934
- this.itemToDelete = null;
2426
+ this.ItemToDelete = null;
1935
2427
  // Clear selection
1936
- this.selectedItems = [];
2428
+ this.SelectedItems = [];
1937
2429
  this.notifications.CreateSimpleNotification(`Deleted "${deletedName}"`, 'info');
1938
2430
  // If we deleted a folder, notify parent that folder structure changed
1939
2431
  if (wasFolder) {
1940
2432
  console.log('[FileGrid] Emitting folderStructureChanged event');
1941
- this.folderStructureChanged.emit();
2433
+ this.FolderStructureChanged.emit();
1942
2434
  }
1943
2435
  // Refresh the file grid
1944
2436
  console.log('[FileGrid] Calling loadItems() to refresh grid');
@@ -1946,7 +2438,7 @@ export class FileGridComponent {
1946
2438
  }
1947
2439
  else {
1948
2440
  console.error('[FileGrid] Delete operation returned false');
1949
- this.notifications.CreateSimpleNotification(`Failed to delete ${this.itemToDelete.type}`, 'error');
2441
+ this.notifications.CreateSimpleNotification(`Failed to delete ${this.ItemToDelete.Type}`, 'error');
1950
2442
  }
1951
2443
  }
1952
2444
  catch (error) {
@@ -1955,76 +2447,88 @@ export class FileGridComponent {
1955
2447
  this.notifications.CreateSimpleNotification(`Failed to delete: ${errorMessage}`, 'error');
1956
2448
  }
1957
2449
  finally {
1958
- this.isDeleting = false;
2450
+ this.IsDeleting = false;
1959
2451
  }
1960
2452
  }
2453
+ /** @deprecated Use {@link OnConfirmDelete}. */
2454
+ async onConfirmDelete() {
2455
+ return this.OnConfirmDelete();
2456
+ }
1961
2457
  /**
1962
2458
  * Opens the rename dialog for the selected item
1963
2459
  */
1964
- onRenameClick() {
1965
- if (this.selectedItems.length !== 1) {
2460
+ OnRenameClick() {
2461
+ if (this.SelectedItems.length !== 1) {
1966
2462
  return;
1967
2463
  }
1968
2464
  // selectedItems contains keys (strings), not full objects
1969
2465
  // Find the actual item object from the items array
1970
- const selectedKey = this.selectedItems[0];
1971
- const item = this.items.find(i => i.key === selectedKey);
2466
+ const selectedKey = this.SelectedItems[0];
2467
+ const item = this.Items.find(i => i.Key === selectedKey);
1972
2468
  if (!item) {
1973
2469
  console.error('[FileGrid] Could not find selected item with key:', selectedKey);
1974
2470
  return;
1975
2471
  }
1976
- this.itemToRename = item;
1977
- this.newItemName = item.name;
1978
- this.showRenameDialog = true;
2472
+ this.ItemToRename = item;
2473
+ this.NewItemName = item.name;
2474
+ this.ShowRenameDialog = true;
2475
+ }
2476
+ /** @deprecated Use {@link OnRenameClick}. */
2477
+ onRenameClick() {
2478
+ return this.OnRenameClick();
1979
2479
  }
1980
2480
  /**
1981
2481
  * Closes the rename dialog
1982
2482
  */
2483
+ OnCancelRename() {
2484
+ this.ShowRenameDialog = false;
2485
+ this.ItemToRename = null;
2486
+ this.NewItemName = '';
2487
+ }
2488
+ /** @deprecated Use {@link OnCancelRename}. */
1983
2489
  onCancelRename() {
1984
- this.showRenameDialog = false;
1985
- this.itemToRename = null;
1986
- this.newItemName = '';
2490
+ return this.OnCancelRename();
1987
2491
  }
1988
2492
  /**
1989
2493
  * Renames the selected item after confirmation
1990
2494
  */
1991
- async onConfirmRename() {
1992
- if (!this.account || !this.itemToRename || !this.newItemName.trim()) {
2495
+ async OnConfirmRename() {
2496
+ if (!this.Account || !this.ItemToRename || !this.NewItemName.trim()) {
1993
2497
  return;
1994
2498
  }
1995
2499
  // Check if name actually changed
1996
- if (this.newItemName.trim() === this.itemToRename.name) {
1997
- this.onCancelRename();
2500
+ if (this.NewItemName.trim() === this.ItemToRename.name) {
2501
+ this.OnCancelRename();
1998
2502
  return;
1999
2503
  }
2000
- this.isRenaming = true;
2504
+ this.IsRenaming = true;
2001
2505
  try {
2002
2506
  // Construct the old and new paths
2003
- const oldPath = this.constructItemPath(this.itemToRename);
2507
+ const oldPath = this.constructItemPath(this.ItemToRename);
2004
2508
  // Build the new path by replacing the old name with the new name
2005
2509
  const pathParts = oldPath.split('/');
2006
- pathParts[pathParts.length - 1] = this.newItemName.trim();
2510
+ pathParts[pathParts.length - 1] = this.NewItemName.trim();
2007
2511
  const newPath = pathParts.join('/');
2008
2512
  console.log('[FileGrid] Renaming item:', { oldPath, newPath });
2009
- const success = await this.storageClient.MoveObject(this.account.account.ID, oldPath, newPath);
2513
+ const success = await this.storageClient.MoveObject(this.Account.account.ID, oldPath, newPath);
2010
2514
  console.log('[FileGrid] Rename result:', { success });
2011
2515
  if (success) {
2012
2516
  console.log('[FileGrid] Item renamed successfully:', { oldPath, newPath });
2013
2517
  // Check if we renamed a folder (before clearing itemToRename)
2014
- const wasFolder = this.itemToRename.type === 'folder';
2015
- const oldName = this.itemToRename.name;
2016
- const newName = this.newItemName.trim();
2518
+ const wasFolder = this.ItemToRename.Type === 'folder';
2519
+ const oldName = this.ItemToRename.name;
2520
+ const newName = this.NewItemName.trim();
2017
2521
  // Close dialog
2018
- this.showRenameDialog = false;
2019
- this.itemToRename = null;
2020
- this.newItemName = '';
2522
+ this.ShowRenameDialog = false;
2523
+ this.ItemToRename = null;
2524
+ this.NewItemName = '';
2021
2525
  // Clear selection
2022
- this.selectedItems = [];
2526
+ this.SelectedItems = [];
2023
2527
  this.notifications.CreateSimpleNotification(`Renamed "${oldName}" to "${newName}"`, 'success');
2024
2528
  // If we renamed a folder, notify parent that folder structure changed
2025
2529
  if (wasFolder) {
2026
2530
  console.log('[FileGrid] Emitting folderStructureChanged event');
2027
- this.folderStructureChanged.emit();
2531
+ this.FolderStructureChanged.emit();
2028
2532
  }
2029
2533
  // Refresh the file grid
2030
2534
  console.log('[FileGrid] Calling loadItems() to refresh grid');
@@ -2032,7 +2536,7 @@ export class FileGridComponent {
2032
2536
  }
2033
2537
  else {
2034
2538
  console.error('[FileGrid] Rename operation returned false');
2035
- this.notifications.CreateSimpleNotification(`Failed to rename ${this.itemToRename.type}`, 'error');
2539
+ this.notifications.CreateSimpleNotification(`Failed to rename ${this.ItemToRename.Type}`, 'error');
2036
2540
  }
2037
2541
  }
2038
2542
  catch (error) {
@@ -2041,35 +2545,39 @@ export class FileGridComponent {
2041
2545
  this.notifications.CreateSimpleNotification(`Failed to rename: ${errorMessage}`, 'error');
2042
2546
  }
2043
2547
  finally {
2044
- this.isRenaming = false;
2548
+ this.IsRenaming = false;
2045
2549
  }
2046
2550
  }
2551
+ /** @deprecated Use {@link OnConfirmRename}. */
2552
+ async onConfirmRename() {
2553
+ return this.OnConfirmRename();
2554
+ }
2047
2555
  /**
2048
2556
  * Downloads the selected file
2049
2557
  */
2050
- async onDownloadClick() {
2051
- if (this.selectedItems.length !== 1) {
2558
+ async OnDownloadClick() {
2559
+ if (this.SelectedItems.length !== 1) {
2052
2560
  return;
2053
2561
  }
2054
2562
  // Find the selected item
2055
- const selectedKey = this.selectedItems[0];
2056
- const item = this.items.find(i => i.key === selectedKey);
2563
+ const selectedKey = this.SelectedItems[0];
2564
+ const item = this.Items.find(i => i.Key === selectedKey);
2057
2565
  if (!item) {
2058
2566
  console.error('[FileGrid] Could not find selected item with key:', selectedKey);
2059
2567
  return;
2060
2568
  }
2061
2569
  // Can only download files, not folders
2062
- if (item.type === 'folder') {
2570
+ if (item.Type === 'folder') {
2063
2571
  this.notifications.CreateSimpleNotification('Cannot download folders. Please select a file.', 'warning');
2064
2572
  return;
2065
2573
  }
2066
- if (!this.account) {
2574
+ if (!this.Account) {
2067
2575
  return;
2068
2576
  }
2069
2577
  try {
2070
2578
  const itemPath = this.constructItemPath(item);
2071
2579
  console.log('[FileGrid] Creating download URL for:', itemPath);
2072
- const downloadUrl = await this.storageClient.CreatePreAuthDownloadUrl(this.account.account.ID, itemPath);
2580
+ const downloadUrl = await this.storageClient.CreatePreAuthDownloadUrl(this.Account.account.ID, itemPath);
2073
2581
  if (downloadUrl) {
2074
2582
  console.log('[FileGrid] Download URL created, opening in new tab');
2075
2583
  window.open(downloadUrl, '_blank', 'noopener,noreferrer');
@@ -2085,49 +2593,61 @@ export class FileGridComponent {
2085
2593
  this.notifications.CreateSimpleNotification(`Failed to download: ${errorMessage}`, 'error');
2086
2594
  }
2087
2595
  }
2596
+ /** @deprecated Use {@link OnDownloadClick}. */
2597
+ async onDownloadClick() {
2598
+ return this.OnDownloadClick();
2599
+ }
2088
2600
  /**
2089
2601
  * Opens the copy dialog for the selected item
2090
2602
  */
2091
- onCopyClick() {
2092
- if (this.selectedItems.length !== 1) {
2603
+ OnCopyClick() {
2604
+ if (this.SelectedItems.length !== 1) {
2093
2605
  return;
2094
2606
  }
2095
- const selectedKey = this.selectedItems[0];
2096
- const item = this.items.find(i => i.key === selectedKey);
2607
+ const selectedKey = this.SelectedItems[0];
2608
+ const item = this.Items.find(i => i.Key === selectedKey);
2097
2609
  if (!item) {
2098
2610
  console.error('[FileGrid] Could not find selected item with key:', selectedKey);
2099
2611
  return;
2100
2612
  }
2101
- this.itemToCopy = item;
2102
- this.copyDestinationPath = this.folderPath + item.name + '-copy';
2103
- this.showCopyDialog = true;
2613
+ this.ItemToCopy = item;
2614
+ this.CopyDestinationPath = this.FolderPath + item.name + '-copy';
2615
+ this.ShowCopyDialog = true;
2616
+ }
2617
+ /** @deprecated Use {@link OnCopyClick}. */
2618
+ onCopyClick() {
2619
+ return this.OnCopyClick();
2104
2620
  }
2105
2621
  /**
2106
2622
  * Cancels the copy operation
2107
2623
  */
2624
+ OnCancelCopy() {
2625
+ this.ShowCopyDialog = false;
2626
+ this.ItemToCopy = null;
2627
+ this.CopyDestinationPath = '';
2628
+ }
2629
+ /** @deprecated Use {@link OnCancelCopy}. */
2108
2630
  onCancelCopy() {
2109
- this.showCopyDialog = false;
2110
- this.itemToCopy = null;
2111
- this.copyDestinationPath = '';
2631
+ return this.OnCancelCopy();
2112
2632
  }
2113
2633
  /**
2114
2634
  * Confirms and executes the copy operation
2115
2635
  */
2116
- async onConfirmCopy() {
2117
- if (!this.account || !this.itemToCopy || !this.copyDestinationPath.trim()) {
2636
+ async OnConfirmCopy() {
2637
+ if (!this.Account || !this.ItemToCopy || !this.CopyDestinationPath.trim()) {
2118
2638
  return;
2119
2639
  }
2120
- this.isCopying = true;
2640
+ this.IsCopying = true;
2121
2641
  try {
2122
- const sourcePath = this.constructItemPath(this.itemToCopy);
2123
- const success = await this.storageClient.CopyObject(this.account.account.ID, sourcePath, this.copyDestinationPath.trim());
2642
+ const sourcePath = this.constructItemPath(this.ItemToCopy);
2643
+ const success = await this.storageClient.CopyObject(this.Account.account.ID, sourcePath, this.CopyDestinationPath.trim());
2124
2644
  if (success) {
2125
2645
  // Close dialog
2126
- this.showCopyDialog = false;
2127
- this.itemToCopy = null;
2128
- this.copyDestinationPath = '';
2646
+ this.ShowCopyDialog = false;
2647
+ this.ItemToCopy = null;
2648
+ this.CopyDestinationPath = '';
2129
2649
  // Clear selection
2130
- this.selectedItems = [];
2650
+ this.SelectedItems = [];
2131
2651
  this.notifications.CreateSimpleNotification('Item copied successfully', 'success');
2132
2652
  // Refresh the file grid
2133
2653
  this.loadItems();
@@ -2142,61 +2662,73 @@ export class FileGridComponent {
2142
2662
  this.notifications.CreateSimpleNotification(`Failed to copy: ${errorMessage}`, 'error');
2143
2663
  }
2144
2664
  finally {
2145
- this.isCopying = false;
2665
+ this.IsCopying = false;
2146
2666
  }
2147
2667
  }
2668
+ /** @deprecated Use {@link OnConfirmCopy}. */
2669
+ async onConfirmCopy() {
2670
+ return this.OnConfirmCopy();
2671
+ }
2148
2672
  /**
2149
2673
  * Opens the move dialog for the selected item
2150
2674
  */
2151
- onMoveClick() {
2152
- if (this.selectedItems.length !== 1) {
2675
+ OnMoveClick() {
2676
+ if (this.SelectedItems.length !== 1) {
2153
2677
  return;
2154
2678
  }
2155
- const selectedKey = this.selectedItems[0];
2156
- const item = this.items.find(i => i.key === selectedKey);
2679
+ const selectedKey = this.SelectedItems[0];
2680
+ const item = this.Items.find(i => i.Key === selectedKey);
2157
2681
  if (!item) {
2158
2682
  console.error('[FileGrid] Could not find selected item with key:', selectedKey);
2159
2683
  return;
2160
2684
  }
2161
- this.itemToMove = item;
2685
+ this.ItemToMove = item;
2162
2686
  // Suggest a different folder path
2163
- this.moveDestinationPath = this.folderPath;
2164
- this.showMoveDialog = true;
2687
+ this.MoveDestinationPath = this.FolderPath;
2688
+ this.ShowMoveDialog = true;
2689
+ }
2690
+ /** @deprecated Use {@link OnMoveClick}. */
2691
+ onMoveClick() {
2692
+ return this.OnMoveClick();
2165
2693
  }
2166
2694
  /**
2167
2695
  * Cancels the move operation
2168
2696
  */
2697
+ OnCancelMove() {
2698
+ this.ShowMoveDialog = false;
2699
+ this.ItemToMove = null;
2700
+ this.MoveDestinationPath = '';
2701
+ }
2702
+ /** @deprecated Use {@link OnCancelMove}. */
2169
2703
  onCancelMove() {
2170
- this.showMoveDialog = false;
2171
- this.itemToMove = null;
2172
- this.moveDestinationPath = '';
2704
+ return this.OnCancelMove();
2173
2705
  }
2174
2706
  /**
2175
2707
  * Confirms and executes the move operation
2176
2708
  */
2177
- async onConfirmMove() {
2178
- if (!this.account || !this.itemToMove || !this.moveDestinationPath.trim()) {
2709
+ async OnConfirmMove() {
2710
+ if (!this.Account || !this.ItemToMove || !this.MoveDestinationPath.trim()) {
2179
2711
  return;
2180
2712
  }
2181
- this.isMoving = true;
2713
+ this.IsMoving = true;
2182
2714
  try {
2183
- const sourcePath = this.constructItemPath(this.itemToMove);
2184
- const destPath = this.moveDestinationPath.trim().endsWith('/')
2185
- ? this.moveDestinationPath.trim() + this.itemToMove.name
2186
- : this.moveDestinationPath.trim();
2187
- const success = await this.storageClient.MoveObject(this.account.account.ID, sourcePath, destPath);
2715
+ const sourcePath = this.constructItemPath(this.ItemToMove);
2716
+ const destPath = this.MoveDestinationPath.trim().endsWith('/')
2717
+ ? this.MoveDestinationPath.trim() + this.ItemToMove.name
2718
+ : this.MoveDestinationPath.trim();
2719
+ const success = await this.storageClient.MoveObject(this.Account.account.ID, sourcePath, destPath);
2188
2720
  if (success) {
2189
- const wasFolder = this.itemToMove.type === 'folder';
2721
+ const wasFolder = this.ItemToMove.Type === 'folder';
2190
2722
  // Close dialog
2191
- this.showMoveDialog = false;
2192
- this.itemToMove = null;
2193
- this.moveDestinationPath = '';
2723
+ this.ShowMoveDialog = false;
2724
+ this.ItemToMove = null;
2725
+ this.MoveDestinationPath = '';
2194
2726
  // Clear selection
2195
- this.selectedItems = [];
2727
+ this.SelectedItems = [];
2196
2728
  this.notifications.CreateSimpleNotification('Item moved successfully', 'success');
2197
2729
  // If we moved a folder, notify parent that folder structure changed
2198
2730
  if (wasFolder) {
2199
- this.folderStructureChanged.emit();
2731
+ this.FolderStructureChanged.emit();
2200
2732
  }
2201
2733
  // Refresh the file grid
2202
2734
  this.loadItems();
@@ -2211,32 +2743,36 @@ export class FileGridComponent {
2211
2743
  this.notifications.CreateSimpleNotification(`Failed to move: ${errorMessage}`, 'error');
2212
2744
  }
2213
2745
  finally {
2214
- this.isMoving = false;
2746
+ this.IsMoving = false;
2215
2747
  }
2216
2748
  }
2749
+ /** @deprecated Use {@link OnConfirmMove}. */
2750
+ async onConfirmMove() {
2751
+ return this.OnConfirmMove();
2752
+ }
2217
2753
  /**
2218
2754
  * Constructs the full path for an item
2219
2755
  */
2220
2756
  constructItemPath(item) {
2221
2757
  // Validate that we have either key or name
2222
- if (!item.key && !item.name) {
2758
+ if (!item.Key && !item.name) {
2223
2759
  console.error('[FileGrid] Cannot construct path - item has no key or name:', item);
2224
2760
  throw new Error('Cannot construct path for item without key or name');
2225
2761
  }
2226
2762
  // If item.key exists and is at root level, use it directly
2227
- if (!this.folderPath || this.folderPath === '/') {
2228
- return item.key || item.name;
2763
+ if (!this.FolderPath || this.FolderPath === '/') {
2764
+ return item.Key || item.name;
2229
2765
  }
2230
2766
  // If the item key already includes the folder path, use it as is
2231
- if (item.key && item.key.startsWith(this.folderPath)) {
2232
- return item.key;
2767
+ if (item.Key && item.Key.startsWith(this.FolderPath)) {
2768
+ return item.Key;
2233
2769
  }
2234
2770
  // Otherwise, combine folder path with item name
2235
- const normalizedFolderPath = this.folderPath.endsWith('/')
2236
- ? this.folderPath.slice(0, -1)
2237
- : this.folderPath;
2771
+ const normalizedFolderPath = this.FolderPath.endsWith('/')
2772
+ ? this.FolderPath.slice(0, -1)
2773
+ : this.FolderPath;
2238
2774
  // Make sure we have a name to use
2239
- const itemName = item.name || item.key;
2775
+ const itemName = item.name || item.Key;
2240
2776
  if (!itemName) {
2241
2777
  console.error('[FileGrid] Cannot construct path - item has no usable name:', item);
2242
2778
  throw new Error('Cannot construct path for item without name');
@@ -2246,96 +2782,116 @@ export class FileGridComponent {
2246
2782
  /**
2247
2783
  * Gets the currently selected item (if exactly one is selected)
2248
2784
  */
2249
- getSelectedItem() {
2250
- if (this.selectedItems.length !== 1) {
2785
+ GetSelectedItem() {
2786
+ if (this.SelectedItems.length !== 1) {
2251
2787
  return null;
2252
2788
  }
2253
- return this.items.find(item => item.key === this.selectedItems[0]) || null;
2789
+ return this.Items.find(item => item.Key === this.SelectedItems[0]) || null;
2790
+ }
2791
+ /** @deprecated Use {@link GetSelectedItem}. */
2792
+ getSelectedItem() {
2793
+ return this.GetSelectedItem();
2254
2794
  }
2255
2795
  /**
2256
2796
  * Opens the copy to account dialog
2257
2797
  */
2258
- async onCopyToAccountClick() {
2259
- const item = this.getSelectedItem();
2260
- if (!item || item.type === 'folder') {
2798
+ async OnCopyToAccountClick() {
2799
+ const item = this.GetSelectedItem();
2800
+ if (!item || item.Type === 'folder') {
2261
2801
  return;
2262
2802
  }
2263
2803
  try {
2264
2804
  const engine = FileStorageEngineBase.Instance;
2265
2805
  await engine.Config(false); // Use cached data if available
2266
2806
  // Build available accounts (excluding current account)
2267
- this.availableAccounts = engine.AccountsWithProviders
2268
- .filter(a => !UUIDsEqual(a.account.ID, this.account?.account.ID));
2269
- if (this.availableAccounts.length === 0) {
2807
+ this.AvailableAccounts = engine.AccountsWithProviders
2808
+ .filter(a => !UUIDsEqual(a.account.ID, this.Account?.account.ID));
2809
+ if (this.AvailableAccounts.length === 0) {
2270
2810
  this.notifications.CreateSimpleNotification('No other storage accounts available', 'warning');
2271
2811
  return;
2272
2812
  }
2273
- this.itemToCopyToProvider = item;
2274
- this.copyToAccountDestinationPath = item.name; // Default to same filename
2275
- this.selectedDestinationAccounts.clear();
2276
- this.copyToAccountProgress = null;
2277
- this.showCopyToProviderDialog = true;
2813
+ this.ItemToCopyToProvider = item;
2814
+ this.CopyToAccountDestinationPath = item.name; // Default to same filename
2815
+ this.SelectedDestinationAccounts.clear();
2816
+ this.CopyToAccountProgress = null;
2817
+ this.ShowCopyToProviderDialog = true;
2278
2818
  }
2279
2819
  catch (error) {
2280
2820
  console.error('[FileGrid] Error loading accounts:', error);
2281
2821
  this.notifications.CreateSimpleNotification('Failed to load storage accounts', 'error');
2282
2822
  }
2283
2823
  }
2824
+ /** @deprecated Use {@link OnCopyToAccountClick}. */
2825
+ async onCopyToAccountClick() {
2826
+ return this.OnCopyToAccountClick();
2827
+ }
2284
2828
  /**
2285
2829
  * Cancels the copy to account dialog
2286
2830
  */
2831
+ OnCancelCopyToAccount() {
2832
+ this.ShowCopyToProviderDialog = false;
2833
+ this.ItemToCopyToProvider = null;
2834
+ this.SelectedDestinationAccounts.clear();
2835
+ this.CopyToAccountDestinationPath = '';
2836
+ this.CopyToAccountProgress = null;
2837
+ }
2838
+ /** @deprecated Use {@link OnCancelCopyToAccount}. */
2287
2839
  onCancelCopyToAccount() {
2288
- this.showCopyToProviderDialog = false;
2289
- this.itemToCopyToProvider = null;
2290
- this.selectedDestinationAccounts.clear();
2291
- this.copyToAccountDestinationPath = '';
2292
- this.copyToAccountProgress = null;
2840
+ return this.OnCancelCopyToAccount();
2293
2841
  }
2294
2842
  /**
2295
2843
  * Toggles selection of a destination account for copying
2296
2844
  */
2297
- toggleDestinationAccount(accountId) {
2298
- if (this.selectedDestinationAccounts.has(accountId)) {
2299
- this.selectedDestinationAccounts.delete(accountId);
2845
+ ToggleDestinationAccount(accountId) {
2846
+ if (this.SelectedDestinationAccounts.has(accountId)) {
2847
+ this.SelectedDestinationAccounts.delete(accountId);
2300
2848
  }
2301
2849
  else {
2302
- this.selectedDestinationAccounts.add(accountId);
2850
+ this.SelectedDestinationAccounts.add(accountId);
2303
2851
  }
2304
2852
  }
2853
+ /** @deprecated Use {@link ToggleDestinationAccount}. */
2854
+ toggleDestinationAccount(accountId) {
2855
+ return this.ToggleDestinationAccount(accountId);
2856
+ }
2305
2857
  /**
2306
2858
  * Checks if an account is selected as a destination
2307
2859
  */
2860
+ IsDestinationAccountSelected(accountId) {
2861
+ return this.SelectedDestinationAccounts.has(accountId);
2862
+ }
2863
+ /** @deprecated Use {@link IsDestinationAccountSelected}. */
2308
2864
  isDestinationAccountSelected(accountId) {
2309
- return this.selectedDestinationAccounts.has(accountId);
2865
+ return this.IsDestinationAccountSelected(accountId);
2310
2866
  }
2311
2867
  /**
2312
2868
  * Executes the cross-account copy to multiple selected accounts
2313
2869
  */
2314
- async onConfirmCopyToAccount() {
2315
- if (!this.itemToCopyToProvider || this.selectedDestinationAccounts.size === 0 || !this.account) {
2870
+ async OnConfirmCopyToAccount() {
2871
+ if (!this.ItemToCopyToProvider || this.SelectedDestinationAccounts.size === 0 || !this.Account) {
2316
2872
  return;
2317
2873
  }
2318
- this.isCopyingToAccount = true;
2874
+ this.IsCopyingToAccount = true;
2319
2875
  // Get selected accounts
2320
- const selectedAccounts = this.availableAccounts.filter(a => this.selectedDestinationAccounts.has(a.account.ID));
2876
+ const selectedAccounts = this.AvailableAccounts.filter(a => this.SelectedDestinationAccounts.has(a.account.ID));
2321
2877
  const successfulCopies = [];
2322
2878
  const failedCopies = [];
2323
2879
  try {
2324
2880
  // Construct source path and normalize it
2325
- const rawSourcePath = this.constructItemPath(this.itemToCopyToProvider);
2881
+ const rawSourcePath = this.constructItemPath(this.ItemToCopyToProvider);
2326
2882
  const sourcePath = rawSourcePath.replace(/^\/+|\/+$/g, '').replace(/\/+/g, '/');
2327
2883
  console.log('[FileGrid] Cross-account copy sourcePath:', { raw: rawSourcePath, normalized: sourcePath });
2328
2884
  // Copy to each selected account
2329
2885
  for (let i = 0; i < selectedAccounts.length; i++) {
2330
2886
  const destAccount = selectedAccounts[i];
2331
- this.copyToAccountProgress = {
2887
+ this.CopyToAccountProgress = {
2332
2888
  current: i + 1,
2333
2889
  total: selectedAccounts.length,
2334
2890
  currentAccount: destAccount.account.Name
2335
2891
  };
2336
2892
  try {
2337
2893
  console.log('[FileGrid] Copying to account:', destAccount.account.Name);
2338
- const copyResult = await this.storageClient.CopyObjectBetweenAccounts(this.account.account.ID, destAccount.account.ID, sourcePath, this.copyToAccountDestinationPath);
2894
+ const copyResult = await this.storageClient.CopyObjectBetweenAccounts(this.Account.account.ID, destAccount.account.ID, sourcePath, this.CopyToAccountDestinationPath);
2339
2895
  if (copyResult.success) {
2340
2896
  successfulCopies.push(destAccount.account.Name);
2341
2897
  }
@@ -2359,11 +2915,11 @@ export class FileGridComponent {
2359
2915
  this.notifications.CreateSimpleNotification(`Copied to ${successfulCopies.length} account(s). Failed: ${failedCopies.map(f => f.account).join(', ')}`, 'warning');
2360
2916
  }
2361
2917
  // Close dialog
2362
- this.showCopyToProviderDialog = false;
2363
- this.itemToCopyToProvider = null;
2364
- this.selectedDestinationAccounts.clear();
2365
- this.copyToAccountDestinationPath = '';
2366
- this.copyToAccountProgress = null;
2918
+ this.ShowCopyToProviderDialog = false;
2919
+ this.ItemToCopyToProvider = null;
2920
+ this.SelectedDestinationAccounts.clear();
2921
+ this.CopyToAccountDestinationPath = '';
2922
+ this.CopyToAccountProgress = null;
2367
2923
  }
2368
2924
  catch (error) {
2369
2925
  console.error('[FileGrid] Error copying to accounts:', error);
@@ -2371,34 +2927,42 @@ export class FileGridComponent {
2371
2927
  this.notifications.CreateSimpleNotification(`Copy failed: ${errorMessage}`, 'error');
2372
2928
  }
2373
2929
  finally {
2374
- this.isCopyingToAccount = false;
2375
- this.copyToAccountProgress = null;
2930
+ this.IsCopyingToAccount = false;
2931
+ this.CopyToAccountProgress = null;
2376
2932
  }
2377
2933
  }
2934
+ /** @deprecated Use {@link OnConfirmCopyToAccount}. */
2935
+ async onConfirmCopyToAccount() {
2936
+ return this.OnConfirmCopyToAccount();
2937
+ }
2378
2938
  // ==========================================
2379
2939
  // Multi-Account Search Methods
2380
2940
  // ==========================================
2381
2941
  /**
2382
2942
  * Toggles multi-account search mode
2383
2943
  */
2384
- toggleMultiAccountSearchMode() {
2385
- this.isMultiProviderSearchMode = !this.isMultiProviderSearchMode;
2386
- if (this.isMultiProviderSearchMode) {
2944
+ ToggleMultiAccountSearchMode() {
2945
+ this.IsMultiProviderSearchMode = !this.IsMultiProviderSearchMode;
2946
+ if (this.IsMultiProviderSearchMode) {
2387
2947
  // Load available accounts if not already loaded
2388
- if (this.availableAccounts.length === 0) {
2948
+ if (this.AvailableAccounts.length === 0) {
2389
2949
  this.loadAvailableAccountsForSearch();
2390
2950
  }
2391
2951
  // Pre-select current account if available
2392
- if (this.account) {
2393
- this.selectedSearchProviders.add(this.account.account.ID);
2952
+ if (this.Account) {
2953
+ this.SelectedSearchProviders.add(this.Account.account.ID);
2394
2954
  }
2395
2955
  }
2396
2956
  else {
2397
2957
  // Clear search results when exiting search mode
2398
- this.multiProviderSearchResults = null;
2399
- this.multiProviderSearchQuery = '';
2958
+ this.MultiProviderSearchResults = null;
2959
+ this.MultiProviderSearchQuery = '';
2400
2960
  }
2401
2961
  }
2962
+ /** @deprecated Use {@link ToggleMultiAccountSearchMode}. */
2963
+ toggleMultiAccountSearchMode() {
2964
+ return this.ToggleMultiAccountSearchMode();
2965
+ }
2402
2966
  /**
2403
2967
  * Loads available accounts for search selection
2404
2968
  */
@@ -2406,7 +2970,7 @@ export class FileGridComponent {
2406
2970
  try {
2407
2971
  const engine = FileStorageEngineBase.Instance;
2408
2972
  await engine.Config(false); // Use cached data if available
2409
- this.availableAccounts = engine.AccountsWithProviders;
2973
+ this.AvailableAccounts = engine.AccountsWithProviders;
2410
2974
  }
2411
2975
  catch (error) {
2412
2976
  console.error('[FileGrid] Error loading accounts for search:', error);
@@ -2415,45 +2979,57 @@ export class FileGridComponent {
2415
2979
  /**
2416
2980
  * Toggles account selection for search
2417
2981
  */
2418
- toggleSearchAccount(accountID) {
2419
- if (this.selectedSearchProviders.has(accountID)) {
2420
- this.selectedSearchProviders.delete(accountID);
2982
+ ToggleSearchAccount(accountID) {
2983
+ if (this.SelectedSearchProviders.has(accountID)) {
2984
+ this.SelectedSearchProviders.delete(accountID);
2421
2985
  }
2422
2986
  else {
2423
- this.selectedSearchProviders.add(accountID);
2987
+ this.SelectedSearchProviders.add(accountID);
2424
2988
  }
2425
2989
  }
2990
+ /** @deprecated Use {@link ToggleSearchAccount}. */
2991
+ toggleSearchAccount(accountID) {
2992
+ return this.ToggleSearchAccount(accountID);
2993
+ }
2426
2994
  /**
2427
2995
  * Checks if an account is selected for search
2428
2996
  */
2997
+ IsAccountSelectedForSearch(accountID) {
2998
+ return this.SelectedSearchProviders.has(accountID);
2999
+ }
3000
+ /** @deprecated Use {@link IsAccountSelectedForSearch}. */
2429
3001
  isAccountSelectedForSearch(accountID) {
2430
- return this.selectedSearchProviders.has(accountID);
3002
+ return this.IsAccountSelectedForSearch(accountID);
2431
3003
  }
2432
3004
  /**
2433
3005
  * Checks if an account's provider supports search
2434
3006
  */
2435
- accountSupportsSearch(accountWithProvider) {
3007
+ AccountSupportsSearch(accountWithProvider) {
2436
3008
  return accountWithProvider.provider.SupportsSearch === true;
2437
3009
  }
3010
+ /** @deprecated Use {@link AccountSupportsSearch}. */
3011
+ accountSupportsSearch(accountWithProvider) {
3012
+ return this.AccountSupportsSearch(accountWithProvider);
3013
+ }
2438
3014
  /**
2439
3015
  * Executes multi-account search
2440
3016
  */
2441
- async executeMultiAccountSearch() {
2442
- if (!this.multiProviderSearchQuery.trim() || this.selectedSearchProviders.size === 0) {
3017
+ async ExecuteMultiAccountSearch() {
3018
+ if (!this.MultiProviderSearchQuery.trim() || this.SelectedSearchProviders.size === 0) {
2443
3019
  return;
2444
3020
  }
2445
- this.isSearching = true;
2446
- this.multiProviderSearchResults = null;
3021
+ this.IsSearching = true;
3022
+ this.MultiProviderSearchResults = null;
2447
3023
  try {
2448
3024
  console.log('[FileGrid] Executing multi-account search:', {
2449
- accountIds: Array.from(this.selectedSearchProviders),
2450
- query: this.multiProviderSearchQuery
3025
+ accountIds: Array.from(this.SelectedSearchProviders),
3026
+ query: this.MultiProviderSearchQuery
2451
3027
  });
2452
- const searchResult = await this.storageClient.SearchFiles(Array.from(this.selectedSearchProviders), this.multiProviderSearchQuery, { maxResultsPerAccount: 50 });
3028
+ const searchResult = await this.storageClient.SearchFiles(Array.from(this.SelectedSearchProviders), this.MultiProviderSearchQuery, { maxResultsPerAccount: 50 });
2453
3029
  console.log('[FileGrid] Multi-account search result:', searchResult);
2454
3030
  // Map the client result to the component's expected format
2455
- this.multiProviderSearchResults = {
2456
- accountResults: searchResult.accountResults.map((ar) => ({
3031
+ this.MultiProviderSearchResults = {
3032
+ AccountResults: searchResult.accountResults.map((ar) => ({
2457
3033
  accountID: ar.accountId,
2458
3034
  accountName: ar.accountName,
2459
3035
  success: ar.success,
@@ -2473,9 +3049,9 @@ export class FileGridComponent {
2473
3049
  hasMore: ar.hasMore,
2474
3050
  nextPageToken: ar.nextPageToken
2475
3051
  })),
2476
- totalResultsReturned: searchResult.totalResultsReturned,
2477
- successfulAccounts: searchResult.successfulAccounts,
2478
- failedAccounts: searchResult.failedAccounts
3052
+ TotalResultsReturned: searchResult.totalResultsReturned,
3053
+ SuccessfulAccounts: searchResult.successfulAccounts,
3054
+ FailedAccounts: searchResult.failedAccounts
2479
3055
  };
2480
3056
  }
2481
3057
  catch (error) {
@@ -2488,20 +3064,28 @@ export class FileGridComponent {
2488
3064
  }, 5000);
2489
3065
  }
2490
3066
  finally {
2491
- this.isSearching = false;
3067
+ this.IsSearching = false;
2492
3068
  }
2493
3069
  }
3070
+ /** @deprecated Use {@link ExecuteMultiAccountSearch}. */
3071
+ async executeMultiAccountSearch() {
3072
+ return this.ExecuteMultiAccountSearch();
3073
+ }
2494
3074
  /**
2495
3075
  * Clears multi-provider search results
2496
3076
  */
3077
+ ClearMultiProviderSearch() {
3078
+ this.MultiProviderSearchQuery = '';
3079
+ this.MultiProviderSearchResults = null;
3080
+ }
3081
+ /** @deprecated Use {@link ClearMultiProviderSearch}. */
2497
3082
  clearMultiProviderSearch() {
2498
- this.multiProviderSearchQuery = '';
2499
- this.multiProviderSearchResults = null;
3083
+ return this.ClearMultiProviderSearch();
2500
3084
  }
2501
3085
  /**
2502
3086
  * Gets icon for a search result based on content type
2503
3087
  */
2504
- getSearchResultIcon(result) {
3088
+ GetSearchResultIcon(result) {
2505
3089
  const extension = result.name.split('.').pop()?.toLowerCase() || '';
2506
3090
  if (['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg'].includes(extension)) {
2507
3091
  return 'fa-solid fa-file-image';
@@ -2531,24 +3115,36 @@ export class FileGridComponent {
2531
3115
  return 'fa-solid fa-file';
2532
3116
  }
2533
3117
  }
3118
+ /** @deprecated Use {@link GetSearchResultIcon}. */
3119
+ getSearchResultIcon(result) {
3120
+ return this.GetSearchResultIcon(result);
3121
+ }
2534
3122
  /**
2535
3123
  * Formats file size for search results
2536
3124
  */
3125
+ FormatSearchResultSize(bytes) {
3126
+ return this.FormatFileSize(bytes);
3127
+ }
3128
+ /** @deprecated Use {@link FormatSearchResultSize}. */
2537
3129
  formatSearchResultSize(bytes) {
2538
- return this.formatFileSize(bytes);
3130
+ return this.FormatSearchResultSize(bytes);
2539
3131
  }
2540
3132
  /**
2541
3133
  * Formats date for search results
2542
3134
  */
2543
- formatSearchResultDate(dateStr) {
3135
+ FormatSearchResultDate(dateStr) {
2544
3136
  return new Date(dateStr).toLocaleString();
2545
3137
  }
3138
+ /** @deprecated Use {@link FormatSearchResultDate}. */
3139
+ formatSearchResultDate(dateStr) {
3140
+ return this.FormatSearchResultDate(dateStr);
3141
+ }
2546
3142
  /**
2547
3143
  * Determines media category for a file item
2548
3144
  */
2549
- getMediaType(item) {
3145
+ GetMediaType(item) {
2550
3146
  const ext = item.name.split('.').pop()?.toLowerCase() || '';
2551
- const mime = item.contentType?.toLowerCase() || '';
3147
+ const mime = item.ContentType?.toLowerCase() || '';
2552
3148
  if (mime.startsWith('image/') || ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico'].includes(ext)) {
2553
3149
  return 'image';
2554
3150
  }
@@ -2572,21 +3168,29 @@ export class FileGridComponent {
2572
3168
  }
2573
3169
  return 'unknown';
2574
3170
  }
3171
+ /** @deprecated Use {@link GetMediaType}. */
3172
+ getMediaType(item) {
3173
+ return this.GetMediaType(item);
3174
+ }
2575
3175
  /**
2576
3176
  * Gets specific badge class for media type
2577
3177
  */
2578
- getItemColorClass(item) {
2579
- if (item.type === 'folder') {
3178
+ GetItemColorClass(item) {
3179
+ if (item.Type === 'folder') {
2580
3180
  return 'mj-file-thumb--folder';
2581
3181
  }
2582
- const mediaType = this.getMediaType(item);
3182
+ const mediaType = this.GetMediaType(item);
2583
3183
  return `mj-file-thumb--${mediaType}`;
2584
3184
  }
3185
+ /** @deprecated Use {@link GetItemColorClass}. */
3186
+ getItemColorClass(item) {
3187
+ return this.GetItemColorClass(item);
3188
+ }
2585
3189
  /**
2586
3190
  * Generates clickable path breadcrumbs
2587
3191
  */
2588
- getPathBreadcrumbs() {
2589
- const raw = (this.folderPath || '').trim();
3192
+ GetPathBreadcrumbs() {
3193
+ const raw = (this.FolderPath || '').trim();
2590
3194
  if (!raw || raw === '/') {
2591
3195
  return [{ name: 'Root', path: '/' }];
2592
3196
  }
@@ -2602,87 +3206,119 @@ export class FileGridComponent {
2602
3206
  }
2603
3207
  return crumbs;
2604
3208
  }
3209
+ /** @deprecated Use {@link GetPathBreadcrumbs}. */
3210
+ getPathBreadcrumbs() {
3211
+ return this.GetPathBreadcrumbs();
3212
+ }
2605
3213
  /**
2606
3214
  * Navigates to a specific breadcrumb path
2607
3215
  */
3216
+ NavigateToBreadcrumb(path) {
3217
+ this.FolderNavigate.emit(path);
3218
+ }
3219
+ /** @deprecated Use {@link NavigateToBreadcrumb}. */
2608
3220
  navigateToBreadcrumb(path) {
2609
- this.folderNavigate.emit(path);
3221
+ return this.NavigateToBreadcrumb(path);
2610
3222
  }
2611
3223
  /**
2612
3224
  * Toggles selection of a file/folder item
2613
3225
  */
2614
- toggleItemSelection(item, event) {
3226
+ ToggleItemSelection(item, event) {
2615
3227
  if (event) {
2616
3228
  event.stopPropagation();
2617
3229
  }
2618
- const idx = this.selectedItems.indexOf(item.key);
3230
+ const idx = this.SelectedItems.indexOf(item.Key);
2619
3231
  if (idx >= 0) {
2620
- this.selectedItems = this.selectedItems.filter((k) => k !== item.key);
3232
+ this.SelectedItems = this.SelectedItems.filter((k) => k !== item.Key);
2621
3233
  }
2622
3234
  else {
2623
- this.selectedItems = [...this.selectedItems, item.key];
3235
+ this.SelectedItems = [...this.SelectedItems, item.Key];
2624
3236
  }
2625
3237
  this.cdr.markForCheck();
2626
3238
  }
3239
+ /** @deprecated Use {@link ToggleItemSelection}. */
3240
+ toggleItemSelection(item, event) {
3241
+ return this.ToggleItemSelection(item, event);
3242
+ }
2627
3243
  /**
2628
3244
  * Selects all current filtered items
2629
3245
  */
2630
- selectAll() {
2631
- this.selectedItems = this.filteredItems.map((i) => i.key);
3246
+ SelectAll() {
3247
+ this.SelectedItems = this.FilteredItems.map((i) => i.Key);
2632
3248
  this.cdr.markForCheck();
2633
3249
  }
3250
+ /** @deprecated Use {@link SelectAll}. */
3251
+ selectAll() {
3252
+ return this.SelectAll();
3253
+ }
2634
3254
  /**
2635
3255
  * Clears selection
2636
3256
  */
2637
- deselectAll() {
2638
- this.selectedItems = [];
3257
+ DeselectAll() {
3258
+ this.SelectedItems = [];
2639
3259
  this.cdr.markForCheck();
2640
3260
  }
3261
+ /** @deprecated Use {@link DeselectAll}. */
3262
+ deselectAll() {
3263
+ return this.DeselectAll();
3264
+ }
2641
3265
  /**
2642
3266
  * Checks if all items are selected
2643
3267
  */
3268
+ AreAllSelected() {
3269
+ return (this.FilteredItems.length > 0 &&
3270
+ this.SelectedItems.length === this.FilteredItems.length);
3271
+ }
3272
+ /** @deprecated Use {@link AreAllSelected}. */
2644
3273
  areAllSelected() {
2645
- return (this.filteredItems.length > 0 &&
2646
- this.selectedItems.length === this.filteredItems.length);
3274
+ return this.AreAllSelected();
2647
3275
  }
2648
3276
  /**
2649
3277
  * Opens in-browser preview modal
2650
3278
  */
2651
- async openPreview(item) {
2652
- if (item.type === 'folder' || !this.account) {
3279
+ async OpenPreview(item) {
3280
+ if (item.Type === 'folder' || !this.Account) {
2653
3281
  return;
2654
3282
  }
2655
- this.previewItem = item;
2656
- this.previewMediaType = this.getMediaType(item);
2657
- this.showPreviewModal = true;
2658
- this.isLoadingPreview = true;
2659
- this.previewUrl = null;
3283
+ this.PreviewItem = item;
3284
+ this.PreviewMediaType = this.GetMediaType(item);
3285
+ this.ShowPreviewModal = true;
3286
+ this.IsLoadingPreview = true;
3287
+ this.PreviewUrl = null;
2660
3288
  this.cdr.markForCheck();
2661
3289
  try {
2662
3290
  const itemPath = this.constructItemPath(item);
2663
- const url = await this.storageClient.CreatePreAuthDownloadUrl(this.account.account.ID, itemPath);
2664
- this.previewUrl = url;
3291
+ const url = await this.storageClient.CreatePreAuthDownloadUrl(this.Account.account.ID, itemPath);
3292
+ this.PreviewUrl = url;
2665
3293
  }
2666
3294
  catch (err) {
2667
3295
  console.error('[FileGrid] Preview error:', err);
2668
3296
  }
2669
3297
  finally {
2670
- this.isLoadingPreview = false;
3298
+ this.IsLoadingPreview = false;
2671
3299
  this.cdr.markForCheck();
2672
3300
  }
2673
3301
  }
3302
+ /** @deprecated Use {@link OpenPreview}. */
3303
+ async openPreview(item) {
3304
+ return this.OpenPreview(item);
3305
+ }
2674
3306
  /**
2675
3307
  * Closes the preview modal
2676
3308
  */
2677
- closePreview() {
2678
- this.showPreviewModal = false;
2679
- this.previewItem = null;
2680
- this.previewUrl = null;
2681
- this.isLoadingPreview = false;
3309
+ ClosePreview() {
3310
+ this.ShowPreviewModal = false;
3311
+ this.PreviewItem = null;
3312
+ this.PreviewUrl = null;
3313
+ this.IsLoadingPreview = false;
2682
3314
  this.cdr.markForCheck();
2683
3315
  }
3316
+ /** @deprecated Use {@link ClosePreview}. */
3317
+ closePreview() {
3318
+ return this.ClosePreview();
3319
+ }
2684
3320
  static ɵfac = function FileGridComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || FileGridComponent)(); };
2685
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FileGridComponent, selectors: [["mj-file-grid"]], inputs: { account: "account", folderPath: "folderPath" }, outputs: { folderNavigate: "folderNavigate", folderStructureChanged: "folderStructureChanged" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 19, vars: 18, consts: [[1, "file-grid-container", 3, "dragenter", "dragover", "dragleave", "drop"], [1, "command-bar", "breadcrumb-bar"], [1, "selection-bar"], [1, "multi-search-card", "multi-provider-search-panel"], [1, "grid-loading-container", "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Unable to load files", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "file-grid-empty-fill", 3, "Message"], [1, "file-cards-grid", "file-grid", "file-grid-wrapper"], [1, "file-table-container", "file-table-wrapper"], ["Icon", "fa-solid fa-folder-open", "Title", "This folder is empty", "Message", "Drop files here to upload or create a new folder", "ActionText", "Upload Files", "ActionIcon", "fa-solid fa-cloud-arrow-up"], [1, "status-bar"], [1, "drag-drop-overlay"], [1, "upload-progress-bar-wrap"], [1, "mj-modal-backdrop"], [1, "mj-modal-backdrop", "modal-overlay"], [1, "command-bar-left"], [1, "nav-btn-group", "nav-buttons"], ["type", "button", "title", "Go up to parent folder", 1, "mj-tool-btn", "nav-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-arrow-up"], ["type", "button", "title", "Refresh current folder", 1, "mj-tool-btn", "nav-btn", 3, "click"], [1, "fa-solid", "fa-arrows-rotate"], ["aria-label", "Folder Path", 1, "breadcrumb-trail", "breadcrumb-path"], [1, "fa-solid", "fa-folder-open", "trail-root-icon"], [1, "command-bar-right"], [1, "search-pill", "search-bar"], [1, "fa-solid", "fa-magnifying-glass", "search-icon"], ["type", "text", "placeholder", "Search files...", 1, "search-input", 3, "ngModelChange", "ngModel", "disabled"], ["type", "button", "title", "Clear search", 1, "search-clear-btn"], [1, "type-filter-select", "file-type-filter", 3, "ngModelChange", "ngModel", "disabled"], ["value", "all"], ["value", "files"], ["value", "folders"], ["type", "button", "title", "Search across all storage accounts", 1, "mj-btn-search-toggle", "multi-search-btn", 3, "click"], [1, "fa-solid", "fa-layer-group"], [1, "view-mode-toggle", "view-toggle"], ["type", "button", "title", "List View", 1, "view-mode-btn", "view-btn", 3, "click"], [1, "fa-solid", "fa-list"], ["type", "button", "title", "Grid View", 1, "view-mode-btn", "view-btn", 3, "click"], [1, "fa-solid", "fa-table-cells-large"], ["type", "button", "title", "Create New Folder", 1, "mj-action-btn", "mj-action-btn--secondary", "toolbar-btn", 3, "click"], [1, "fa-solid", "fa-folder-plus"], ["type", "button", "title", "Upload Files", 1, "mj-action-btn", "mj-action-btn--primary", "toolbar-btn", 3, "click"], [1, "fa-solid", "fa-cloud-arrow-up"], ["type", "button", 1, "crumb-btn", 3, "click", "title"], [1, "crumb-separator"], ["type", "button", "title", "Clear search", 1, "search-clear-btn", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "selection-bar-left"], [1, "selection-checkbox-wrap", 3, "click"], ["type", "checkbox", 3, "change", "checked"], [1, "selection-count"], [1, "selection-bar-right"], ["type", "button", "title", "Delete selected item(s)", 1, "sel-action-btn", "sel-action-btn--danger", 3, "click"], [1, "fa-solid", "fa-trash"], ["type", "button", "title", "Deselect all", 1, "sel-action-btn", "sel-action-btn--ghost", 3, "click"], ["type", "button", "title", "Preview file", 1, "sel-action-btn", 3, "click"], [1, "fa-solid", "fa-eye"], ["type", "button", "title", "Download file", 1, "sel-action-btn", 3, "click"], [1, "fa-solid", "fa-download"], ["type", "button", "title", "Copy to another storage account", 1, "sel-action-btn", 3, "click"], ["type", "button", "title", "Rename item", 1, "sel-action-btn", 3, "click"], [1, "fa-solid", "fa-pen"], ["type", "button", "title", "Copy to path", 1, "sel-action-btn", 3, "click"], [1, "fa-solid", "fa-copy"], ["type", "button", "title", "Move item", 1, "sel-action-btn", 3, "click"], [1, "fa-solid", "fa-arrows-up-down-left-right"], [1, "multi-search-header", "search-panel-header"], [1, "multi-search-title"], ["type", "button", "title", "Close search", 1, "modal-close-btn", "close-panel-btn", 3, "click"], [1, "multi-search-body", "search-panel-body"], [1, "multi-search-bar", "search-input-row"], ["type", "text", "placeholder", "Type query to search across connected accounts...", 1, "multi-search-text", "multi-search-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], ["type", "button", 1, "mj-action-btn", "mj-action-btn--primary", "search-execute-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-magnifying-glass"], [1, "multi-search-accounts", "provider-selection"], [1, "account-select-label", "provider-selection-label"], [1, "account-pill-list", "provider-checkboxes"], [1, "account-select-pill", "provider-checkbox", 3, "disabled", "checked"], [1, "multi-search-results", "search-results-container"], [1, "account-select-pill", "provider-checkbox"], ["type", "checkbox", 3, "change", "checked", "disabled"], [1, "pill-account-name", "provider-name"], [1, "pill-provider-tag", "provider-type"], [1, "pill-no-search", "no-search-badge"], [1, "results-meta-bar", "search-results-summary"], [1, "meta-count", "result-count"], [1, "meta-stats", "provider-stats"], ["type", "button", 1, "clear-results-btn", 3, "click"], [1, "results-groups", "provider-results-list"], [1, "result-group", "provider-result-group", 3, "failed"], [1, "result-group", "provider-result-group"], [1, "result-group-head", "provider-result-header"], [1, "fa-solid", "fa-cloud-bolt", "group-icon"], [1, "group-account-name", "provider-name"], [1, "group-count-badge", "result-count"], [1, "group-error-badge", "error-badge"], [1, "result-items-grid", "provider-result-files"], [1, "group-empty-text", "no-results-message"], [1, "search-item-row", "search-result-item"], [1, "search-item-icon", "file-icon"], [1, "search-item-info", "file-info"], [1, "search-item-name", "file-name"], [1, "search-item-path", "file-path"], [1, "search-item-size", "file-size"], [1, "search-item-date", "file-date"], ["text", "Loading files..."], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Unable to load files", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "file-grid-empty-fill", 3, "Action", "Message"], [1, "file-card", "grid-item", 3, "selected", "title"], [1, "file-card", "grid-item", 3, "click", "dblclick", "title"], [1, "file-card-header"], [1, "file-card-checkbox", 3, "click"], [1, "file-card-quick-actions", 3, "click"], [1, "file-card-icon-container", "grid-icon"], [1, "file-card-body"], [1, "file-card-name", "grid-name"], [1, "file-card-meta", "grid-meta"], [1, "meta-size"], [1, "meta-date"], ["type", "button", "title", "Preview", 1, "quick-action-btn", 3, "click"], ["type", "button", "title", "Download", 1, "quick-action-btn", 3, "click"], [1, "file-table"], [1, "th-check"], [1, "th-name", "col-name", 3, "click"], [1, "fa-solid", "sort-arrow", "sort-icon"], [1, "th-type", "col-type"], [1, "th-size", "col-size", 3, "click"], [1, "th-date", "col-modified", 3, "click"], [1, "th-actions"], [1, "file-table-row", "file-row", 3, "selected"], [1, "file-table-row", "file-row", 3, "click", "dblclick"], [1, "td-check", 3, "click"], [1, "td-name", "col-name"], [1, "row-name-wrap"], [1, "row-icon-badge"], [1, "item-icon"], [1, "row-file-name", "item-name", 3, "title"], [1, "td-type", "col-type"], [1, "type-badge", "item-type"], [1, "td-size", "col-size"], [1, "size-text", "item-size"], [1, "td-date", "col-modified"], [1, "date-text", "item-date"], [1, "td-actions", 3, "click"], [1, "row-action-buttons"], ["type", "button", "title", "Preview", 1, "row-act-btn", 3, "click"], ["type", "button", "title", "Download", 1, "row-act-btn", 3, "click"], ["Icon", "fa-solid fa-folder-open", "Title", "This folder is empty", "Message", "Drop files here to upload or create a new folder", "ActionText", "Upload Files", "ActionIcon", "fa-solid fa-cloud-arrow-up", 3, "Action"], [1, "status-count"], ["class", "status-account-badge", 4, "ngIf"], [1, "status-account-badge"], [1, "fa-solid", "fa-cloud"], [1, "drag-drop-content"], [1, "fa-solid", "fa-cloud-arrow-up", "drag-icon"], [1, "upload-progress-card"], [1, "upload-info"], [1, "fa-solid", "fa-spinner", "fa-spin", "upload-spinner"], [1, "upload-text"], [1, "progress-track"], [1, "progress-fill"], [1, "mj-modal-backdrop", 3, "click"], [1, "mj-modal-window", "preview-modal-window", 3, "click"], [1, "mj-modal-header"], [1, "modal-title-wrap"], [1, "modal-title"], [1, "modal-subtitle"], [1, "modal-header-actions"], ["type", "button", "title", "Download file", 1, "mj-action-btn", "mj-action-btn--secondary", 3, "click"], ["type", "button", "title", "Close preview", 1, "modal-close-btn", 3, "click"], [1, "preview-modal-body"], [1, "preview-loading"], [1, "preview-fallback"], ["text", "Loading preview..."], [1, "preview-media-wrap", "image-preview-wrap"], [1, "preview-media-wrap", "pdf-preview-wrap"], [1, "preview-media-wrap", "video-preview-wrap"], [1, "preview-media-wrap", "audio-preview-wrap"], [1, "preview-img", 3, "src", "alt"], ["title", "PDF Preview", 1, "preview-iframe", 3, "src"], ["controls", "", "autoplay", "", 1, "preview-video", 3, "src"], [1, "fa-solid", "fa-music", "preview-audio-icon"], ["controls", "", 1, "preview-audio", 3, "src"], [1, "preview-fallback-icon"], ["type", "button", 1, "mj-action-btn", "mj-action-btn--primary", 3, "click"], [1, "fa-solid", "fa-triangle-exclamation", "preview-fallback-icon"], [1, "mj-modal-backdrop", "modal-overlay", 3, "click"], [1, "mj-modal-window", "modal-compact", "modal-dialog", 3, "click"], [1, "mj-modal-header", "modal-header"], ["type", "button", 1, "modal-close-btn", "modal-close", 3, "click"], [1, "mj-modal-body", "modal-body"], [1, "form-label"], ["type", "text", "placeholder", "Enter folder name", "autofocus", "", 1, "form-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "mj-modal-footer", "modal-footer"], ["type", "button", 1, "mj-action-btn", "mj-action-btn--secondary", 3, "click", "disabled"], ["type", "button", 1, "mj-action-btn", "mj-action-btn--primary", 3, "click", "disabled"], [1, "modal-body-text"], [1, "modal-warning-banner", "warning"], ["type", "button", 1, "mj-action-btn", "mj-action-btn--danger", "danger", 3, "click", "disabled"], [1, "fa-solid", "fa-triangle-exclamation"], ["type", "text", "placeholder", "Enter new name", "autofocus", "", 1, "form-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "mj-modal-window", "modal-compact", 3, "click"], ["type", "button", 1, "modal-close-btn", 3, "click"], [1, "mj-modal-body"], ["type", "text", "placeholder", "path/to/destination", 1, "form-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "mj-modal-footer"], ["type", "text", "placeholder", "destination/folder/", 1, "form-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "mj-modal-window", 3, "click"], [1, "account-picker-list"], [1, "account-picker-item", 3, "for"], [1, "form-label", 2, "margin-top", "14px"], ["type", "text", "placeholder", "Filename", 1, "form-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "copy-progress-indicator"], ["type", "checkbox", 3, "change", "id", "checked", "disabled"], [1, "picker-name"], [1, "picker-badge"]], template: function FileGridComponent_Template(rf, ctx) { if (rf & 1) {
3321
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: FileGridComponent, selectors: [["mj-file-grid"]], inputs: { Account: "Account", account: "account", FolderPath: "FolderPath", folderPath: "folderPath" }, outputs: { FolderNavigate: "FolderNavigate", folderNavigate: "folderNavigate", FolderStructureChanged: "FolderStructureChanged", folderStructureChanged: "folderStructureChanged" }, standalone: false, features: [i0.ɵɵNgOnChangesFeature], decls: 19, vars: 18, consts: [[1, "file-grid-container", 3, "dragenter", "dragover", "dragleave", "drop"], [1, "command-bar", "breadcrumb-bar"], [1, "selection-bar"], [1, "multi-search-card", "multi-provider-search-panel"], [1, "grid-loading-container", "loading-state"], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Unable to load files", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "file-grid-empty-fill", 3, "Message"], [1, "file-cards-grid", "file-grid", "file-grid-wrapper"], [1, "file-table-container", "file-table-wrapper"], ["Icon", "fa-solid fa-folder-open", "Title", "This folder is empty", "Message", "Drop files here to upload or create a new folder", "ActionText", "Upload Files", "ActionIcon", "fa-solid fa-cloud-arrow-up"], [1, "status-bar"], [1, "drag-drop-overlay"], [1, "upload-progress-bar-wrap"], [1, "mj-modal-backdrop"], [1, "mj-modal-backdrop", "modal-overlay"], [1, "command-bar-left"], [1, "nav-btn-group", "nav-buttons"], ["type", "button", "title", "Go up to parent folder", 1, "mj-tool-btn", "nav-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-arrow-up"], ["type", "button", "title", "Refresh current folder", 1, "mj-tool-btn", "nav-btn", 3, "click"], [1, "fa-solid", "fa-arrows-rotate"], ["aria-label", "Folder Path", 1, "breadcrumb-trail", "breadcrumb-path"], [1, "fa-solid", "fa-folder-open", "trail-root-icon"], [1, "command-bar-right"], [1, "search-pill", "search-bar"], [1, "fa-solid", "fa-magnifying-glass", "search-icon"], ["type", "text", "placeholder", "Search files...", 1, "search-input", 3, "ngModelChange", "ngModel", "disabled"], ["type", "button", "title", "Clear search", 1, "search-clear-btn"], [1, "type-filter-select", "file-type-filter", 3, "ngModelChange", "ngModel", "disabled"], ["value", "all"], ["value", "files"], ["value", "folders"], ["type", "button", "title", "Search across all storage accounts", 1, "mj-btn-search-toggle", "multi-search-btn", 3, "click"], [1, "fa-solid", "fa-layer-group"], [1, "view-mode-toggle", "view-toggle"], ["type", "button", "title", "List View", 1, "view-mode-btn", "view-btn", 3, "click"], [1, "fa-solid", "fa-list"], ["type", "button", "title", "Grid View", 1, "view-mode-btn", "view-btn", 3, "click"], [1, "fa-solid", "fa-table-cells-large"], ["type", "button", "title", "Create New Folder", 1, "mj-action-btn", "mj-action-btn--secondary", "toolbar-btn", 3, "click"], [1, "fa-solid", "fa-folder-plus"], ["type", "button", "title", "Upload Files", 1, "mj-action-btn", "mj-action-btn--primary", "toolbar-btn", 3, "click"], [1, "fa-solid", "fa-cloud-arrow-up"], ["type", "button", 1, "crumb-btn", 3, "click", "title"], [1, "crumb-separator"], ["type", "button", "title", "Clear search", 1, "search-clear-btn", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "selection-bar-left"], [1, "selection-checkbox-wrap", 3, "click"], ["type", "checkbox", 3, "change", "checked"], [1, "selection-count"], [1, "selection-bar-right"], ["type", "button", "title", "Delete selected item(s)", 1, "sel-action-btn", "sel-action-btn--danger", 3, "click"], [1, "fa-solid", "fa-trash"], ["type", "button", "title", "Deselect all", 1, "sel-action-btn", "sel-action-btn--ghost", 3, "click"], ["type", "button", "title", "Preview file", 1, "sel-action-btn", 3, "click"], [1, "fa-solid", "fa-eye"], ["type", "button", "title", "Download file", 1, "sel-action-btn", 3, "click"], [1, "fa-solid", "fa-download"], ["type", "button", "title", "Copy to another storage account", 1, "sel-action-btn", 3, "click"], ["type", "button", "title", "Rename item", 1, "sel-action-btn", 3, "click"], [1, "fa-solid", "fa-pen"], ["type", "button", "title", "Copy to path", 1, "sel-action-btn", 3, "click"], [1, "fa-solid", "fa-copy"], ["type", "button", "title", "Move item", 1, "sel-action-btn", 3, "click"], [1, "fa-solid", "fa-arrows-up-down-left-right"], [1, "multi-search-header", "search-panel-header"], [1, "multi-search-title"], ["type", "button", "title", "Close search", 1, "modal-close-btn", "close-panel-btn", 3, "click"], [1, "multi-search-body", "search-panel-body"], [1, "multi-search-bar", "search-input-row"], ["type", "text", "placeholder", "Type query to search across connected accounts...", 1, "multi-search-text", "multi-search-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], ["type", "button", 1, "mj-action-btn", "mj-action-btn--primary", "search-execute-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-magnifying-glass"], [1, "multi-search-accounts", "provider-selection"], [1, "account-select-label", "provider-selection-label"], [1, "account-pill-list", "provider-checkboxes"], [1, "account-select-pill", "provider-checkbox", 3, "disabled", "checked"], [1, "multi-search-results", "search-results-container"], [1, "account-select-pill", "provider-checkbox"], ["type", "checkbox", 3, "change", "checked", "disabled"], [1, "pill-account-name", "provider-name"], [1, "pill-provider-tag", "provider-type"], [1, "pill-no-search", "no-search-badge"], [1, "results-meta-bar", "search-results-summary"], [1, "meta-count", "result-count"], [1, "meta-stats", "provider-stats"], ["type", "button", 1, "clear-results-btn", 3, "click"], [1, "results-groups", "provider-results-list"], [1, "result-group", "provider-result-group", 3, "failed"], [1, "result-group", "provider-result-group"], [1, "result-group-head", "provider-result-header"], [1, "fa-solid", "fa-cloud-bolt", "group-icon"], [1, "group-account-name", "provider-name"], [1, "group-count-badge", "result-count"], [1, "group-error-badge", "error-badge"], [1, "result-items-grid", "provider-result-files"], [1, "group-empty-text", "no-results-message"], [1, "search-item-row", "search-result-item"], [1, "search-item-icon", "file-icon"], [1, "search-item-info", "file-info"], [1, "search-item-name", "file-name"], [1, "search-item-path", "file-path"], [1, "search-item-size", "file-size"], [1, "search-item-date", "file-date"], ["text", "Loading files..."], ["Variant", "error", "Icon", "fa-solid fa-triangle-exclamation", "Title", "Unable to load files", "ActionText", "Retry", "ActionIcon", "fa-solid fa-arrows-rotate", 1, "file-grid-empty-fill", 3, "Action", "Message"], [1, "file-card", "grid-item", 3, "selected", "title"], [1, "file-card", "grid-item", 3, "click", "dblclick", "title"], [1, "file-card-header"], [1, "file-card-checkbox", 3, "click"], [1, "file-card-quick-actions", 3, "click"], [1, "file-card-icon-container", "grid-icon"], [1, "file-card-body"], [1, "file-card-name", "grid-name"], [1, "file-card-meta", "grid-meta"], [1, "meta-size"], [1, "meta-date"], ["type", "button", "title", "Preview", 1, "quick-action-btn", 3, "click"], ["type", "button", "title", "Download", 1, "quick-action-btn", 3, "click"], [1, "file-table"], [1, "th-check"], [1, "th-name", "col-name", 3, "click"], [1, "fa-solid", "sort-arrow", "sort-icon"], [1, "th-type", "col-type"], [1, "th-size", "col-size", 3, "click"], [1, "th-date", "col-modified", 3, "click"], [1, "th-actions"], [1, "file-table-row", "file-row", 3, "selected"], [1, "file-table-row", "file-row", 3, "click", "dblclick"], [1, "td-check", 3, "click"], [1, "td-name", "col-name"], [1, "row-name-wrap"], [1, "row-icon-badge"], [1, "item-icon"], [1, "row-file-name", "item-name", 3, "title"], [1, "td-type", "col-type"], [1, "type-badge", "item-type"], [1, "td-size", "col-size"], [1, "size-text", "item-size"], [1, "td-date", "col-modified"], [1, "date-text", "item-date"], [1, "td-actions", 3, "click"], [1, "row-action-buttons"], ["type", "button", "title", "Preview", 1, "row-act-btn", 3, "click"], ["type", "button", "title", "Download", 1, "row-act-btn", 3, "click"], ["Icon", "fa-solid fa-folder-open", "Title", "This folder is empty", "Message", "Drop files here to upload or create a new folder", "ActionText", "Upload Files", "ActionIcon", "fa-solid fa-cloud-arrow-up", 3, "Action"], [1, "status-count"], ["class", "status-account-badge", 4, "ngIf"], [1, "status-account-badge"], [1, "fa-solid", "fa-cloud"], [1, "drag-drop-content"], [1, "fa-solid", "fa-cloud-arrow-up", "drag-icon"], [1, "upload-progress-card"], [1, "upload-info"], [1, "fa-solid", "fa-spinner", "fa-spin", "upload-spinner"], [1, "upload-text"], [1, "progress-track"], [1, "progress-fill"], [1, "mj-modal-backdrop", 3, "click"], [1, "mj-modal-window", "preview-modal-window", 3, "click"], [1, "mj-modal-header"], [1, "modal-title-wrap"], [1, "modal-title"], [1, "modal-subtitle"], [1, "modal-header-actions"], ["type", "button", "title", "Download file", 1, "mj-action-btn", "mj-action-btn--secondary", 3, "click"], ["type", "button", "title", "Close preview", 1, "modal-close-btn", 3, "click"], [1, "preview-modal-body"], [1, "preview-loading"], [1, "preview-fallback"], ["text", "Loading preview..."], [1, "preview-media-wrap", "image-preview-wrap"], [1, "preview-media-wrap", "pdf-preview-wrap"], [1, "preview-media-wrap", "video-preview-wrap"], [1, "preview-media-wrap", "audio-preview-wrap"], [1, "preview-img", 3, "src", "alt"], ["title", "PDF Preview", 1, "preview-iframe", 3, "src"], ["controls", "", "autoplay", "", 1, "preview-video", 3, "src"], [1, "fa-solid", "fa-music", "preview-audio-icon"], ["controls", "", 1, "preview-audio", 3, "src"], [1, "preview-fallback-icon"], ["type", "button", 1, "mj-action-btn", "mj-action-btn--primary", 3, "click"], [1, "fa-solid", "fa-triangle-exclamation", "preview-fallback-icon"], [1, "mj-modal-backdrop", "modal-overlay", 3, "click"], [1, "mj-modal-window", "modal-compact", "modal-dialog", 3, "click"], [1, "mj-modal-header", "modal-header"], ["type", "button", 1, "modal-close-btn", "modal-close", 3, "click"], [1, "mj-modal-body", "modal-body"], [1, "form-label"], ["type", "text", "placeholder", "Enter folder name", "autofocus", "", 1, "form-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "mj-modal-footer", "modal-footer"], ["type", "button", 1, "mj-action-btn", "mj-action-btn--secondary", 3, "click", "disabled"], ["type", "button", 1, "mj-action-btn", "mj-action-btn--primary", 3, "click", "disabled"], [1, "modal-body-text"], [1, "modal-warning-banner", "warning"], ["type", "button", 1, "mj-action-btn", "mj-action-btn--danger", "danger", 3, "click", "disabled"], [1, "fa-solid", "fa-triangle-exclamation"], ["type", "text", "placeholder", "Enter new name", "autofocus", "", 1, "form-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "mj-modal-window", "modal-compact", 3, "click"], ["type", "button", 1, "modal-close-btn", 3, "click"], [1, "mj-modal-body"], ["type", "text", "placeholder", "path/to/destination", 1, "form-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "mj-modal-footer"], ["type", "text", "placeholder", "destination/folder/", 1, "form-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "mj-modal-window", 3, "click"], [1, "account-picker-list"], [1, "account-picker-item", 3, "for"], [1, "form-label", 2, "margin-top", "14px"], ["type", "text", "placeholder", "Filename", 1, "form-input", 3, "ngModelChange", "keydown.enter", "ngModel", "disabled"], [1, "copy-progress-indicator"], ["type", "checkbox", 3, "change", "id", "checked", "disabled"], [1, "picker-name"], [1, "picker-badge"]], template: function FileGridComponent_Template(rf, ctx) { if (rf & 1) {
2686
3322
  i0.ɵɵelementStart(0, "div", 0);
2687
3323
  i0.ɵɵlistener("dragenter", function FileGridComponent_Template_div_dragenter_0_listener($event) { return ctx.onDragEnter($event); })("dragover", function FileGridComponent_Template_div_dragover_0_listener($event) { return ctx.onDragOver($event); })("dragleave", function FileGridComponent_Template_div_dragleave_0_listener($event) { return ctx.onDragLeave($event); })("drop", function FileGridComponent_Template_div_drop_0_listener($event) { return ctx.onDrop($event); });
2688
3324
  i0.ɵɵconditionalCreate(1, FileGridComponent_Conditional_1_Template, 40, 12, "div", 1);
@@ -2745,13 +3381,21 @@ export class FileGridComponent {
2745
3381
  }
2746
3382
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(FileGridComponent, [{
2747
3383
  type: Component,
2748
- args: [{ standalone: false, selector: 'mj-file-grid', template: "<div class=\"file-grid-container\"\n (dragenter)=\"onDragEnter($event)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n\n <!-- Top Command Bar: Breadcrumbs & Primary Actions -->\n @if (!isLoading && !errorMessage) {\n <div class=\"command-bar breadcrumb-bar\">\n <!-- Left: Breadcrumb Navigation Trail -->\n <div class=\"command-bar-left\">\n <div class=\"nav-btn-group nav-buttons\">\n <button\n type=\"button\"\n class=\"mj-tool-btn nav-btn\"\n [disabled]=\"!canNavigateUp()\"\n (click)=\"navigateUp()\"\n title=\"Go up to parent folder\">\n <i class=\"fa-solid fa-arrow-up\"></i>\n </button>\n <button\n type=\"button\"\n class=\"mj-tool-btn nav-btn\"\n (click)=\"refresh()\"\n title=\"Refresh current folder\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n </button>\n </div>\n\n <nav class=\"breadcrumb-trail breadcrumb-path\" aria-label=\"Folder Path\">\n <i class=\"fa-solid fa-folder-open trail-root-icon\"></i>\n @for (crumb of getPathBreadcrumbs(); track crumb.path; let last = $last) {\n <button\n type=\"button\"\n class=\"crumb-btn\"\n [class.current]=\"last\"\n (click)=\"navigateToBreadcrumb(crumb.path)\"\n [title]=\"crumb.name\">\n {{ crumb.name }}\n </button>\n @if (!last) {\n <span class=\"crumb-separator\">/</span>\n }\n }\n </nav>\n </div>\n\n <!-- Right: Search, Filter, View Mode & Action Buttons -->\n <div class=\"command-bar-right\">\n <!-- Search & Filter Bar -->\n <div class=\"search-pill search-bar\">\n <i class=\"fa-solid fa-magnifying-glass search-icon\"></i>\n <input\n type=\"text\"\n class=\"search-input\"\n [(ngModel)]=\"searchQuery\"\n (ngModelChange)=\"onSearchChange($event)\"\n placeholder=\"Search files...\"\n [disabled]=\"isLoading || isMultiProviderSearchMode\" />\n @if (searchQuery && !isMultiProviderSearchMode) {\n <button\n type=\"button\"\n class=\"search-clear-btn\"\n (click)=\"clearSearch()\"\n title=\"Clear search\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n }\n <select\n class=\"type-filter-select file-type-filter\"\n [(ngModel)]=\"fileTypeFilter\"\n (ngModelChange)=\"onFileTypeFilterChange($event)\"\n [disabled]=\"isLoading || isMultiProviderSearchMode\">\n <option value=\"all\">All Items</option>\n <option value=\"files\">Files only</option>\n <option value=\"folders\">Folders only</option>\n </select>\n </div>\n\n <!-- Search Across Accounts Toggle -->\n <button\n type=\"button\"\n class=\"mj-btn-search-toggle multi-search-btn\"\n [class.active]=\"isMultiProviderSearchMode\"\n (click)=\"toggleMultiAccountSearchMode()\"\n title=\"Search across all storage accounts\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>All Accounts</span>\n </button>\n\n <!-- View Switcher -->\n <div class=\"view-mode-toggle view-toggle\">\n <button\n type=\"button\"\n class=\"view-mode-btn view-btn\"\n [class.active]=\"viewMode === 'list'\"\n (click)=\"viewMode = 'list'\"\n title=\"List View\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n <button\n type=\"button\"\n class=\"view-mode-btn view-btn\"\n [class.active]=\"viewMode === 'grid'\"\n (click)=\"viewMode = 'grid'\"\n title=\"Grid View\">\n <i class=\"fa-solid fa-table-cells-large\"></i>\n </button>\n </div>\n\n <!-- Primary Action CTAs -->\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary toolbar-btn\"\n (click)=\"onNewFolderClick()\"\n title=\"Create New Folder\">\n <i class=\"fa-solid fa-folder-plus\"></i>\n <span>New Folder</span>\n </button>\n\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary toolbar-btn\"\n (click)=\"onUploadClick()\"\n title=\"Upload Files\">\n <i class=\"fa-solid fa-cloud-arrow-up\"></i>\n <span>Upload</span>\n </button>\n </div>\n </div>\n }\n\n <!-- Selection Action Bar (Appears when 1+ items selected) -->\n @if (selectedItems.length > 0 && !isMultiProviderSearchMode) {\n <div class=\"selection-bar\">\n <div class=\"selection-bar-left\">\n <label class=\"selection-checkbox-wrap\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"areAllSelected()\"\n (change)=\"areAllSelected() ? deselectAll() : selectAll()\" />\n <span class=\"selection-count\">{{ selectedItems.length }} item{{ selectedItems.length === 1 ? '' : 's' }} selected</span>\n </label>\n </div>\n\n <div class=\"selection-bar-right\">\n @if (selectedItems.length === 1 && getSelectedItem()?.type === 'file') {\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"openPreview(getSelectedItem()!)\"\n title=\"Preview file\">\n <i class=\"fa-solid fa-eye\"></i>\n <span>Preview</span>\n </button>\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"onDownloadClick()\"\n title=\"Download file\">\n <i class=\"fa-solid fa-download\"></i>\n <span>Download</span>\n </button>\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"onCopyToAccountClick()\"\n title=\"Copy to another storage account\">\n <i class=\"fa-solid fa-cloud-arrow-up\"></i>\n <span>Copy to...</span>\n </button>\n }\n @if (selectedItems.length === 1) {\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"onRenameClick()\"\n title=\"Rename item\">\n <i class=\"fa-solid fa-pen\"></i>\n <span>Rename</span>\n </button>\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"onCopyClick()\"\n title=\"Copy to path\">\n <i class=\"fa-solid fa-copy\"></i>\n <span>Copy</span>\n </button>\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"onMoveClick()\"\n title=\"Move item\">\n <i class=\"fa-solid fa-arrows-up-down-left-right\"></i>\n <span>Move</span>\n </button>\n }\n <button\n type=\"button\"\n class=\"sel-action-btn sel-action-btn--danger\"\n (click)=\"onDeleteClick()\"\n title=\"Delete selected item(s)\">\n <i class=\"fa-solid fa-trash\"></i>\n <span>Delete</span>\n </button>\n <button\n type=\"button\"\n class=\"sel-action-btn sel-action-btn--ghost\"\n (click)=\"deselectAll()\"\n title=\"Deselect all\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n </div>\n }\n\n <!-- Multi-Provider Search Panel -->\n @if (isMultiProviderSearchMode) {\n <div class=\"multi-search-card multi-provider-search-panel\">\n <div class=\"multi-search-header search-panel-header\">\n <div class=\"multi-search-title\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <h3>Search Across Storage Accounts</h3>\n </div>\n <button\n type=\"button\"\n class=\"modal-close-btn close-panel-btn\"\n (click)=\"toggleMultiAccountSearchMode()\"\n title=\"Close search\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <div class=\"multi-search-body search-panel-body\">\n <div class=\"multi-search-bar search-input-row\">\n <input\n type=\"text\"\n class=\"multi-search-text multi-search-input\"\n [(ngModel)]=\"multiProviderSearchQuery\"\n placeholder=\"Type query to search across connected accounts...\"\n (keydown.enter)=\"executeMultiAccountSearch()\"\n [disabled]=\"isSearching\" />\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary search-execute-btn\"\n (click)=\"executeMultiAccountSearch()\"\n [disabled]=\"isSearching || !multiProviderSearchQuery.trim() || selectedSearchProviders.size === 0\">\n @if (isSearching) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n }\n <span>{{ isSearching ? 'Searching...' : 'Search Accounts' }}</span>\n </button>\n </div>\n\n <div class=\"multi-search-accounts provider-selection\">\n <span class=\"account-select-label provider-selection-label\">Target Accounts:</span>\n <div class=\"account-pill-list provider-checkboxes\">\n @for (a of availableAccounts; track a.account.ID) {\n <label\n class=\"account-select-pill provider-checkbox\"\n [class.disabled]=\"!accountSupportsSearch(a)\"\n [class.checked]=\"isAccountSelectedForSearch(a.account.ID)\">\n <input\n type=\"checkbox\"\n [checked]=\"isAccountSelectedForSearch(a.account.ID)\"\n (change)=\"toggleSearchAccount(a.account.ID)\"\n [disabled]=\"!accountSupportsSearch(a)\" />\n <span class=\"pill-account-name provider-name\">{{ a.account.Name }}</span>\n <span class=\"pill-provider-tag provider-type\">{{ a.provider.Name }}</span>\n @if (!accountSupportsSearch(a)) {\n <span class=\"pill-no-search no-search-badge\">(no search)</span>\n }\n </label>\n }\n </div>\n </div>\n </div>\n\n <!-- Search Results -->\n @if (multiProviderSearchResults) {\n <div class=\"multi-search-results search-results-container\">\n <div class=\"results-meta-bar search-results-summary\">\n <span class=\"meta-count result-count\">\n <strong>{{ multiProviderSearchResults.totalResultsReturned }}</strong> result{{ multiProviderSearchResults.totalResultsReturned === 1 ? '' : 's' }} found\n </span>\n <span class=\"meta-stats provider-stats\">\n ({{ multiProviderSearchResults.successfulAccounts }} account{{ multiProviderSearchResults.successfulAccounts === 1 ? '' : 's' }} searched)\n </span>\n <button\n type=\"button\"\n class=\"clear-results-btn\"\n (click)=\"clearMultiProviderSearch()\">\n <i class=\"fa-solid fa-xmark\"></i> Clear Results\n </button>\n </div>\n\n <div class=\"results-groups provider-results-list\">\n @for (accountResult of multiProviderSearchResults.accountResults; track accountResult.accountID) {\n <div class=\"result-group provider-result-group\" [class.failed]=\"!accountResult.success\">\n <div class=\"result-group-head provider-result-header\">\n <i class=\"fa-solid fa-cloud-bolt group-icon\"></i>\n <span class=\"group-account-name provider-name\">{{ accountResult.accountName }}</span>\n @if (accountResult.success) {\n <span class=\"group-count-badge result-count\">{{ accountResult.results.length }}</span>\n } @else {\n <span class=\"group-error-badge error-badge\">{{ accountResult.errorMessage }}</span>\n }\n </div>\n\n @if (accountResult.success && accountResult.results.length > 0) {\n <div class=\"result-items-grid provider-result-files\">\n @for (file of accountResult.results; track file.path) {\n <div class=\"search-item-row search-result-item\">\n <i [class]=\"getSearchResultIcon(file)\" class=\"search-item-icon file-icon\"></i>\n <div class=\"search-item-info file-info\">\n <span class=\"search-item-name file-name\">{{ file.name }}</span>\n <span class=\"search-item-path file-path\">{{ file.path }}</span>\n </div>\n <span class=\"search-item-size file-size\">{{ formatSearchResultSize(file.size) }}</span>\n <span class=\"search-item-date file-date\">{{ formatSearchResultDate(file.lastModified) }}</span>\n </div>\n }\n </div>\n }\n @if (accountResult.success && accountResult.results.length === 0) {\n <div class=\"group-empty-text no-results-message\">No matching files found in this account</div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Loading State -->\n @if (isLoading && !isMultiProviderSearchMode) {\n <div class=\"grid-loading-container loading-state\">\n <mj-loading text=\"Loading files...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !isLoading) {\n <mj-empty-state\n class=\"file-grid-empty-fill\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Unable to load files\"\n [Message]=\"errorMessage\"\n ActionText=\"Retry\"\n ActionIcon=\"fa-solid fa-arrows-rotate\"\n (Action)=\"refresh()\" />\n }\n\n <!-- Grid View -->\n @if (!isLoading && !errorMessage && filteredItems.length > 0 && viewMode === 'grid') {\n <div class=\"file-cards-grid file-grid file-grid-wrapper\">\n @for (item of filteredItems; track item.key) {\n <div\n class=\"file-card grid-item\"\n [class.selected]=\"selectedItems.includes(item.key)\"\n (click)=\"onTileClick(item, $event)\"\n (dblclick)=\"onItemDoubleClick(item)\"\n [title]=\"item.name\">\n\n <!-- Card Top Row: Checkbox & Quick Actions -->\n <div class=\"file-card-header\">\n <label class=\"file-card-checkbox\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedItems.includes(item.key)\"\n (change)=\"toggleItemSelection(item, $event)\" />\n </label>\n <div class=\"file-card-quick-actions\" (click)=\"$event.stopPropagation()\">\n @if (item.type === 'file') {\n <button\n type=\"button\"\n class=\"quick-action-btn\"\n (click)=\"openPreview(item)\"\n title=\"Preview\">\n <i class=\"fa-solid fa-eye\"></i>\n </button>\n <button\n type=\"button\"\n class=\"quick-action-btn\"\n (click)=\"downloadFile(item)\"\n title=\"Download\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n }\n </div>\n </div>\n\n <!-- Card Thumbnail/Icon -->\n <div class=\"file-card-icon-container grid-icon\" [class]=\"getItemColorClass(item)\">\n <i [class]=\"getItemIcon(item)\"></i>\n </div>\n\n <!-- Card Body: Name & Meta -->\n <div class=\"file-card-body\">\n <span class=\"file-card-name grid-name\">{{ item.name }}</span>\n <div class=\"file-card-meta grid-meta\">\n <span class=\"meta-size\">{{ item.type === 'folder' ? 'Folder' : formatFileSize(item.size) }}</span>\n <span class=\"meta-date\">{{ formatDate(item.lastModified) }}</span>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- List View Table -->\n @if (!isLoading && !errorMessage && filteredItems.length > 0 && viewMode === 'list') {\n <div class=\"file-table-container file-table-wrapper\">\n <table class=\"file-table\">\n <thead>\n <tr>\n <th class=\"th-check\">\n <input\n type=\"checkbox\"\n [checked]=\"areAllSelected()\"\n (change)=\"areAllSelected() ? deselectAll() : selectAll()\" />\n </th>\n <th class=\"th-name col-name\" (click)=\"onSortChange([{ field: 'name', dir: sort[0].field === 'name' && sort[0].dir === 'asc' ? 'desc' : 'asc' }])\">\n <span>Name</span>\n <i class=\"fa-solid sort-arrow sort-icon\"\n [class.fa-caret-up]=\"sort[0].field === 'name' && sort[0].dir === 'asc'\"\n [class.fa-caret-down]=\"sort[0].field === 'name' && sort[0].dir === 'desc'\"\n [class.fa-sort]=\"sort[0].field !== 'name'\"></i>\n </th>\n <th class=\"th-type col-type\">Type</th>\n <th class=\"th-size col-size\" (click)=\"onSortChange([{ field: 'size', dir: sort[0].field === 'size' && sort[0].dir === 'asc' ? 'desc' : 'asc' }])\">\n <span>Size</span>\n <i class=\"fa-solid sort-arrow sort-icon\"\n [class.fa-caret-up]=\"sort[0].field === 'size' && sort[0].dir === 'asc'\"\n [class.fa-caret-down]=\"sort[0].field === 'size' && sort[0].dir === 'desc'\"\n [class.fa-sort]=\"sort[0].field !== 'size'\"></i>\n </th>\n <th class=\"th-date col-modified\" (click)=\"onSortChange([{ field: 'lastModified', dir: sort[0].field === 'lastModified' && sort[0].dir === 'asc' ? 'desc' : 'asc' }])\">\n <span>Modified</span>\n <i class=\"fa-solid sort-arrow sort-icon\"\n [class.fa-caret-up]=\"sort[0].field === 'lastModified' && sort[0].dir === 'asc'\"\n [class.fa-caret-down]=\"sort[0].field === 'lastModified' && sort[0].dir === 'desc'\"\n [class.fa-sort]=\"sort[0].field !== 'lastModified'\"></i>\n </th>\n <th class=\"th-actions\">Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (item of filteredItems; track item.key) {\n <tr\n class=\"file-table-row file-row\"\n [class.selected]=\"selectedItems.includes(item.key)\"\n (click)=\"onTileClick(item, $event)\"\n (dblclick)=\"onItemDoubleClick(item)\">\n <td class=\"td-check\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedItems.includes(item.key)\"\n (change)=\"toggleItemSelection(item, $event)\" />\n </td>\n <td class=\"td-name col-name\">\n <div class=\"row-name-wrap\">\n <div class=\"row-icon-badge\" [class]=\"getItemColorClass(item)\">\n <i [class]=\"getItemIcon(item)\" class=\"item-icon\"></i>\n </div>\n <span class=\"row-file-name item-name\" [title]=\"item.name\">{{ item.name }}</span>\n </div>\n </td>\n <td class=\"td-type col-type\">\n <span class=\"type-badge item-type\">{{ getFileType(item) }}</span>\n </td>\n <td class=\"td-size col-size\">\n <span class=\"size-text item-size\">{{ item.type === 'folder' ? '\u2014' : formatFileSize(item.size) }}</span>\n </td>\n <td class=\"td-date col-modified\">\n <span class=\"date-text item-date\">{{ formatDate(item.lastModified) }}</span>\n </td>\n <td class=\"td-actions\" (click)=\"$event.stopPropagation()\">\n <div class=\"row-action-buttons\">\n @if (item.type === 'file') {\n <button\n type=\"button\"\n class=\"row-act-btn\"\n (click)=\"openPreview(item)\"\n title=\"Preview\">\n <i class=\"fa-solid fa-eye\"></i>\n </button>\n <button\n type=\"button\"\n class=\"row-act-btn\"\n (click)=\"downloadFile(item)\"\n title=\"Download\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n }\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- Empty State -->\n @if (!isLoading && !errorMessage && filteredItems.length === 0 && !isMultiProviderSearchMode) {\n <mj-empty-state\n Icon=\"fa-solid fa-folder-open\"\n Title=\"This folder is empty\"\n Message=\"Drop files here to upload or create a new folder\"\n ActionText=\"Upload Files\"\n ActionIcon=\"fa-solid fa-cloud-arrow-up\"\n (Action)=\"onUploadClick()\" />\n }\n\n <!-- Bottom Status Bar -->\n @if (!isLoading && !errorMessage) {\n <div class=\"status-bar\">\n <span class=\"status-count\">\n {{ filteredItems.length }} item{{ filteredItems.length === 1 ? '' : 's' }}\n @if (searchQuery) {\n (filtered)\n }\n </span>\n <div class=\"status-account-badge\" *ngIf=\"account\">\n <i class=\"fa-solid fa-cloud\"></i>\n <span>{{ account.account.Name }} ({{ account.provider.Name }})</span>\n </div>\n </div>\n }\n\n <!-- Drag & Drop Upload Overlay -->\n @if (isDragging) {\n <div class=\"drag-drop-overlay\">\n <div class=\"drag-drop-content\">\n <i class=\"fa-solid fa-cloud-arrow-up drag-icon\"></i>\n <h3>Drop files here to upload</h3>\n <p>Files will be stored in {{ folderPath || 'Root' }}</p>\n </div>\n </div>\n }\n\n <!-- Upload Progress Bar -->\n @if (isUploading) {\n <div class=\"upload-progress-bar-wrap\">\n <div class=\"upload-progress-card\">\n <div class=\"upload-info\">\n <i class=\"fa-solid fa-spinner fa-spin upload-spinner\"></i>\n <span class=\"upload-text\">Uploading {{ uploadingFileName }}...</span>\n </div>\n <div class=\"progress-track\">\n <div class=\"progress-fill\" [style.width.%]=\"uploadProgress\"></div>\n </div>\n </div>\n </div>\n }\n</div>\n\n<!-- ==========================================================================\n MODAL DIALOGS (Design Tokens, Smooth Animations, Backdrop Blurs)\n ========================================================================== -->\n\n<!-- File Preview Modal -->\n@if (showPreviewModal && previewItem) {\n <div class=\"mj-modal-backdrop\" (click)=\"closePreview()\">\n <div class=\"mj-modal-window preview-modal-window\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header\">\n <div class=\"modal-title-wrap\">\n <div class=\"row-icon-badge\" [class]=\"getItemColorClass(previewItem)\">\n <i [class]=\"getItemIcon(previewItem)\"></i>\n </div>\n <div>\n <h3 class=\"modal-title\">{{ previewItem.name }}</h3>\n <span class=\"modal-subtitle\">{{ formatFileSize(previewItem.size) }} &bull; {{ formatDate(previewItem.lastModified) }}</span>\n </div>\n </div>\n <div class=\"modal-header-actions\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"downloadFile(previewItem)\"\n title=\"Download file\">\n <i class=\"fa-solid fa-download\"></i>\n <span>Download</span>\n </button>\n <button\n type=\"button\"\n class=\"modal-close-btn\"\n (click)=\"closePreview()\"\n title=\"Close preview\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n </div>\n\n <div class=\"preview-modal-body\">\n @if (isLoadingPreview) {\n <div class=\"preview-loading\">\n <mj-loading text=\"Loading preview...\"></mj-loading>\n </div>\n } @else if (previewUrl) {\n @switch (previewMediaType) {\n @case ('image') {\n <div class=\"preview-media-wrap image-preview-wrap\">\n <img [src]=\"previewUrl\" [alt]=\"previewItem.name\" class=\"preview-img\" />\n </div>\n }\n @case ('pdf') {\n <div class=\"preview-media-wrap pdf-preview-wrap\">\n <iframe [src]=\"previewUrl\" class=\"preview-iframe\" title=\"PDF Preview\"></iframe>\n </div>\n }\n @case ('video') {\n <div class=\"preview-media-wrap video-preview-wrap\">\n <video [src]=\"previewUrl\" controls autoplay class=\"preview-video\"></video>\n </div>\n }\n @case ('audio') {\n <div class=\"preview-media-wrap audio-preview-wrap\">\n <i class=\"fa-solid fa-music preview-audio-icon\"></i>\n <audio [src]=\"previewUrl\" controls class=\"preview-audio\"></audio>\n </div>\n }\n @default {\n <div class=\"preview-fallback\">\n <i [class]=\"getItemIcon(previewItem)\" class=\"preview-fallback-icon\"></i>\n <h4>{{ previewItem.name }}</h4>\n <p>Preview is not supported for this file type.</p>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"downloadFile(previewItem)\">\n <i class=\"fa-solid fa-download\"></i>\n <span>Download File</span>\n </button>\n </div>\n }\n }\n } @else {\n <div class=\"preview-fallback\">\n <i class=\"fa-solid fa-triangle-exclamation preview-fallback-icon\"></i>\n <h4>Preview unavailable</h4>\n <p>Could not generate a preview link for this file.</p>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"downloadFile(previewItem)\">\n <i class=\"fa-solid fa-download\"></i>\n <span>Download File</span>\n </button>\n </div>\n }\n </div>\n </div>\n </div>\n}\n\n<!-- New Folder Modal -->\n@if (showNewFolderDialog) {\n <div class=\"mj-modal-backdrop modal-overlay\" (click)=\"onCancelNewFolder()\">\n <div class=\"mj-modal-window modal-compact modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header modal-header\">\n <h3 class=\"modal-title\">Create New Folder</h3>\n <button type=\"button\" class=\"modal-close-btn modal-close\" (click)=\"onCancelNewFolder()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body modal-body\">\n <label class=\"form-label\">Folder name:</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"newFolderName\"\n [disabled]=\"isCreatingFolder\"\n placeholder=\"Enter folder name\"\n (keydown.enter)=\"onCreateFolder()\"\n autofocus />\n </div>\n <div class=\"mj-modal-footer modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelNewFolder()\"\n [disabled]=\"isCreatingFolder\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"onCreateFolder()\"\n [disabled]=\"!newFolderName.trim() || isCreatingFolder\">\n @if (isCreatingFolder) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isCreatingFolder ? 'Creating...' : 'Create' }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n\n<!-- Delete Confirmation Modal -->\n@if (showDeleteDialog && itemToDelete) {\n <div class=\"mj-modal-backdrop modal-overlay\" (click)=\"onCancelDelete()\">\n <div class=\"mj-modal-window modal-compact modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header modal-header\">\n <h3 class=\"modal-title\">Delete {{ itemToDelete.type === 'folder' ? 'Folder' : 'File' }}?</h3>\n <button type=\"button\" class=\"modal-close-btn modal-close\" (click)=\"onCancelDelete()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body modal-body\">\n <p class=\"modal-body-text\">\n Are you sure you want to delete <strong>{{ itemToDelete.name }}</strong>?\n </p>\n @if (itemToDelete.type === 'folder') {\n <div class=\"modal-warning-banner warning\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>This will delete the folder and all its contents.</span>\n </div>\n }\n </div>\n <div class=\"mj-modal-footer modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelDelete()\"\n [disabled]=\"isDeleting\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--danger danger\"\n (click)=\"onConfirmDelete()\"\n [disabled]=\"isDeleting\">\n @if (isDeleting) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isDeleting ? 'Deleting...' : 'Delete' }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n\n<!-- Rename Modal -->\n@if (showRenameDialog && itemToRename) {\n <div class=\"mj-modal-backdrop modal-overlay\" (click)=\"onCancelRename()\">\n <div class=\"mj-modal-window modal-compact modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header modal-header\">\n <h3 class=\"modal-title\">Rename {{ itemToRename.type === 'folder' ? 'Folder' : 'File' }}</h3>\n <button type=\"button\" class=\"modal-close-btn modal-close\" (click)=\"onCancelRename()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body modal-body\">\n <label class=\"form-label\">New name:</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"newItemName\"\n [disabled]=\"isRenaming\"\n placeholder=\"Enter new name\"\n (keydown.enter)=\"onConfirmRename()\"\n autofocus />\n </div>\n <div class=\"mj-modal-footer modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelRename()\"\n [disabled]=\"isRenaming\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"onConfirmRename()\"\n [disabled]=\"isRenaming || !newItemName.trim()\">\n @if (isRenaming) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isRenaming ? 'Renaming...' : 'Rename' }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n\n<!-- Copy Modal -->\n@if (showCopyDialog && itemToCopy) {\n <div class=\"mj-modal-backdrop\" (click)=\"onCancelCopy()\">\n <div class=\"mj-modal-window modal-compact\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header\">\n <h3 class=\"modal-title\">Copy {{ itemToCopy.type === 'folder' ? 'Folder' : 'File' }}</h3>\n <button type=\"button\" class=\"modal-close-btn\" (click)=\"onCancelCopy()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body\">\n <p class=\"modal-body-text\">Copying <strong>{{ itemToCopy.name }}</strong></p>\n <label class=\"form-label\">Destination Path</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"copyDestinationPath\"\n [disabled]=\"isCopying\"\n placeholder=\"path/to/destination\"\n (keydown.enter)=\"onConfirmCopy()\" />\n </div>\n <div class=\"mj-modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelCopy()\"\n [disabled]=\"isCopying\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"onConfirmCopy()\"\n [disabled]=\"isCopying || !copyDestinationPath.trim()\">\n @if (isCopying) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isCopying ? 'Copying...' : 'Copy' }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n\n<!-- Move Modal -->\n@if (showMoveDialog && itemToMove) {\n <div class=\"mj-modal-backdrop\" (click)=\"onCancelMove()\">\n <div class=\"mj-modal-window modal-compact\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header\">\n <h3 class=\"modal-title\">Move {{ itemToMove.type === 'folder' ? 'Folder' : 'File' }}</h3>\n <button type=\"button\" class=\"modal-close-btn\" (click)=\"onCancelMove()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body\">\n <p class=\"modal-body-text\">Moving <strong>{{ itemToMove.name }}</strong></p>\n <label class=\"form-label\">Destination Folder</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"moveDestinationPath\"\n [disabled]=\"isMoving\"\n placeholder=\"destination/folder/\"\n (keydown.enter)=\"onConfirmMove()\" />\n </div>\n <div class=\"mj-modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelMove()\"\n [disabled]=\"isMoving\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"onConfirmMove()\"\n [disabled]=\"isMoving || !moveDestinationPath.trim()\">\n @if (isMoving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isMoving ? 'Moving...' : 'Move' }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n\n<!-- Copy to Account Modal -->\n@if (showCopyToProviderDialog && itemToCopyToProvider) {\n <div class=\"mj-modal-backdrop\" (click)=\"onCancelCopyToAccount()\">\n <div class=\"mj-modal-window\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header\">\n <h3 class=\"modal-title\">Copy to Another Account</h3>\n <button type=\"button\" class=\"modal-close-btn\" (click)=\"onCancelCopyToAccount()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body\">\n <p class=\"modal-body-text\">\n Copying <strong>{{ itemToCopyToProvider.name }}</strong> from <strong>{{ account?.account?.Name }}</strong>\n </p>\n\n <label class=\"form-label\">Select Destination Storage Accounts</label>\n <div class=\"account-picker-list\">\n @for (a of availableAccounts; track a.account.ID) {\n <label class=\"account-picker-item\" [for]=\"'dest-acc-' + a.account.ID\">\n <input\n type=\"checkbox\"\n [id]=\"'dest-acc-' + a.account.ID\"\n [checked]=\"isDestinationAccountSelected(a.account.ID)\"\n [disabled]=\"isCopyingToAccount\"\n (change)=\"toggleDestinationAccount(a.account.ID)\" />\n <span class=\"picker-name\">{{ a.account.Name }}</span>\n <span class=\"picker-badge\">{{ a.provider.Name }}</span>\n </label>\n }\n </div>\n\n <label class=\"form-label\" style=\"margin-top: 14px;\">Destination Filename</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"copyToAccountDestinationPath\"\n [disabled]=\"isCopyingToAccount\"\n placeholder=\"Filename\"\n (keydown.enter)=\"onConfirmCopyToAccount()\" />\n\n @if (copyToAccountProgress) {\n <div class=\"copy-progress-indicator\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Copying to {{ copyToAccountProgress.currentAccount }} ({{ copyToAccountProgress.current }}/{{ copyToAccountProgress.total }})...</span>\n </div>\n }\n </div>\n <div class=\"mj-modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelCopyToAccount()\"\n [disabled]=\"isCopyingToAccount\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"onConfirmCopyToAccount()\"\n [disabled]=\"isCopyingToAccount || selectedDestinationAccounts.size === 0 || !copyToAccountDestinationPath.trim()\">\n @if (isCopyingToAccount) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isCopyingToAccount ? 'Copying...' : 'Copy to ' + selectedDestinationAccounts.size + ' Account' + (selectedDestinationAccounts.size !== 1 ? 's' : '') }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n", styles: ["/* ==========================================================================\n File Grid & Table Component Styles\n Modern macOS Finder / Cloud Storage with Design Tokens\n ========================================================================== */\n\n:host {\n display: block;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.file-grid-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n position: relative;\n overflow: hidden;\n}\n\n/* ==========================================================================\n Command Bar (Top Navigation, Search & Primary Actions)\n ========================================================================== */\n\n.command-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 10px 16px;\n background-color: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n flex-wrap: wrap;\n}\n\n.command-bar-left {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n flex: 1;\n}\n\n.nav-btn-group {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.mj-tool-btn {\n width: 32px;\n height: 32px;\n min-width: 32px;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mj-tool-btn:hover:not([disabled]) {\n background-color: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.mj-tool-btn[disabled] {\n opacity: 0.4;\n cursor: not-allowed;\n background-color: transparent;\n}\n\n/* Breadcrumb Trail */\n.breadcrumb-trail {\n display: flex;\n align-items: center;\n gap: 4px;\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n min-width: 0;\n padding: 2px 4px;\n}\n\n.breadcrumb-trail::-webkit-scrollbar {\n display: none;\n}\n\n.trail-root-icon {\n color: var(--mj-status-warning, #f59e0b);\n font-size: 0.95rem;\n margin-right: 2px;\n flex-shrink: 0;\n}\n\n.crumb-btn {\n background: transparent;\n border: none;\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n font-weight: 500;\n padding: 4px 8px;\n border-radius: var(--mj-radius-sm, 6px);\n cursor: pointer;\n white-space: nowrap;\n transition: all 0.12s ease;\n}\n\n.crumb-btn:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n}\n\n.crumb-btn.current {\n color: var(--mj-brand-primary);\n font-weight: 600;\n background-color: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.crumb-separator {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n}\n\n/* Command Bar Right */\n.command-bar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n\n/* Search Pill */\n.search-pill {\n display: flex;\n align-items: center;\n background-color: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 4%, var(--mj-bg-surface)));\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full, 9999px);\n padding: 2px 10px;\n gap: 6px;\n transition: border-color 0.15s ease;\n}\n\n.search-pill:focus-within {\n border-color: var(--mj-brand-primary);\n}\n\n.search-icon {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.search-input {\n border: none;\n background: transparent;\n outline: none;\n font-size: 0.82rem;\n color: var(--mj-text-primary);\n width: 150px;\n padding: 4px 0;\n}\n\n.search-input::placeholder {\n color: var(--mj-text-muted);\n}\n\n.search-clear-btn {\n background: transparent;\n border: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n padding: 0 4px;\n font-size: 0.75rem;\n}\n\n.search-clear-btn:hover {\n color: var(--mj-text-primary);\n}\n\n.type-filter-select {\n border: none;\n background: transparent;\n font-size: 0.76rem;\n color: var(--mj-text-secondary);\n outline: none;\n cursor: pointer;\n padding: 2px 4px;\n border-left: 1px solid var(--mj-border-default);\n}\n\n/* Toggle Multi-Search Button */\n.mj-btn-search-toggle {\n display: flex;\n align-items: center;\n gap: 6px;\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-secondary);\n font-size: 0.8rem;\n font-weight: 500;\n padding: 6px 12px;\n border-radius: var(--mj-radius-full, 9999px);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mj-btn-search-toggle:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n}\n\n.mj-btn-search-toggle.active {\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n border-color: var(--mj-brand-primary);\n}\n\n/* View Mode Toggle */\n.view-mode-toggle {\n display: flex;\n background-color: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 4%, var(--mj-bg-surface)));\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n padding: 2px;\n gap: 2px;\n}\n\n.view-mode-btn {\n background: transparent;\n border: none;\n color: var(--mj-text-secondary);\n padding: 5px 8px;\n font-size: 0.8rem;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.view-mode-btn:hover {\n color: var(--mj-text-primary);\n}\n\n.view-mode-btn.active {\n background-color: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);\n}\n\n/* Action Buttons */\n.mj-action-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n font-size: 0.82rem;\n font-weight: 500;\n border-radius: var(--mj-radius-sm, 6px);\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n white-space: nowrap;\n}\n\n.mj-action-btn--primary {\n background-color: var(--mj-brand-primary, #3b82f6);\n color: var(--mj-text-inverse, #ffffff);\n border-color: var(--mj-brand-primary, #3b82f6);\n}\n\n.mj-action-btn--primary:hover:not([disabled]) {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 85%, var(--mj-text-primary, #000000));\n}\n\n.mj-action-btn--secondary {\n background-color: var(--mj-bg-surface);\n border-color: var(--mj-border-default);\n color: var(--mj-text-primary);\n}\n\n.mj-action-btn--secondary:hover:not([disabled]) {\n background-color: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n}\n\n.mj-action-btn--danger {\n background-color: var(--mj-status-error, #ef4444);\n color: var(--mj-text-inverse, #ffffff);\n border-color: var(--mj-status-error, #ef4444);\n}\n\n.mj-action-btn--danger:hover:not([disabled]) {\n background-color: color-mix(in srgb, var(--mj-status-error, #ef4444) 85%, var(--mj-text-primary, #000000));\n}\n\n.mj-action-btn[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* ==========================================================================\n Selection Action Bar\n ========================================================================== */\n\n.selection-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-bottom: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n gap: 12px;\n flex-shrink: 0;\n animation: slideDown 0.18s ease-out;\n}\n\n@keyframes slideDown {\n from { transform: translateY(-100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n\n.selection-bar-left {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.selection-checkbox-wrap {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n user-select: none;\n}\n\n.selection-count {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.selection-bar-right {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.sel-action-btn {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 4px 10px;\n font-size: 0.76rem;\n font-weight: 500;\n border-radius: var(--mj-radius-sm, 6px);\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-primary);\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.sel-action-btn:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.sel-action-btn--danger:hover {\n background-color: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n.sel-action-btn--ghost {\n background: transparent;\n border-color: transparent;\n}\n\n/* ==========================================================================\n Multi-Account Search Card\n ========================================================================== */\n\n.multi-search-card {\n margin: 12px 16px;\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 10px);\n box-shadow: var(--mj-shadow-md, 0 4px 12px rgba(0, 0, 0, 0.08));\n overflow: hidden;\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(-6px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.multi-search-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background-color: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.multi-search-title {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--mj-brand-primary);\n}\n\n.multi-search-title h3 {\n margin: 0;\n font-size: 0.88rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.multi-search-body {\n padding: 14px 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.multi-search-bar {\n display: flex;\n gap: 8px;\n}\n\n.multi-search-text {\n flex: 1;\n padding: 8px 12px;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.85rem;\n outline: none;\n}\n\n.multi-search-text:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.multi-search-accounts {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.account-select-label {\n font-size: 0.74rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n}\n\n.account-pill-list {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.account-select-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: var(--mj-radius-full, 9999px);\n border: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface-sunken);\n font-size: 0.76rem;\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.account-select-pill.checked {\n border-color: var(--mj-brand-primary);\n background-color: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.account-select-pill.disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.pill-account-name {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.pill-provider-tag {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n}\n\n.pill-no-search {\n font-size: 0.68rem;\n color: var(--mj-status-warning);\n}\n\n/* Results */\n.multi-search-results {\n border-top: 1px solid var(--mj-border-default);\n padding: 12px 16px;\n max-height: 400px;\n overflow-y: auto;\n}\n\n.results-meta-bar {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 12px;\n font-size: 0.8rem;\n}\n\n.meta-count {\n color: var(--mj-text-primary);\n}\n\n.meta-stats {\n color: var(--mj-text-secondary);\n}\n\n.clear-results-btn {\n margin-left: auto;\n background: transparent;\n border: none;\n color: var(--mj-text-secondary);\n font-size: 0.76rem;\n cursor: pointer;\n}\n\n.clear-results-btn:hover {\n color: var(--mj-text-primary);\n}\n\n.results-groups {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.result-group {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n overflow: hidden;\n}\n\n.result-group-head {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background-color: var(--mj-bg-surface-sunken);\n font-size: 0.8rem;\n}\n\n.group-account-name {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.group-count-badge {\n margin-left: auto;\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n font-size: 0.7rem;\n font-weight: 600;\n padding: 1px 6px;\n border-radius: 9999px;\n}\n\n.group-error-badge {\n margin-left: auto;\n color: var(--mj-status-error);\n font-size: 0.72rem;\n}\n\n.result-items-grid {\n display: flex;\n flex-direction: column;\n}\n\n.search-item-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n font-size: 0.8rem;\n}\n\n.search-item-row:last-child {\n border-bottom: none;\n}\n\n.search-item-icon {\n color: var(--mj-brand-primary);\n}\n\n.search-item-info {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n}\n\n.search-item-name {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.search-item-path {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n.search-item-size, .search-item-date {\n font-size: 0.74rem;\n color: var(--mj-text-secondary);\n}\n\n.group-empty-text {\n padding: 12px;\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n text-align: center;\n}\n\n/* ==========================================================================\n Cards Grid View\n ========================================================================== */\n\n.file-cards-grid {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));\n gap: 14px;\n align-content: start;\n}\n\n.file-card {\n display: flex;\n flex-direction: column;\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 10px);\n padding: 10px;\n cursor: pointer;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n position: relative;\n user-select: none;\n}\n\n.file-card:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-md, 0 4px 12px rgba(0, 0, 0, 0.08));\n transform: translateY(-2px);\n}\n\n.file-card.selected {\n border-color: var(--mj-brand-primary);\n background-color: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface));\n box-shadow: 0 0 0 1px var(--mj-brand-primary);\n}\n\n.file-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n height: 22px;\n margin-bottom: 6px;\n}\n\n.file-card-checkbox {\n cursor: pointer;\n}\n\n.file-card-quick-actions {\n display: flex;\n gap: 4px;\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.file-card:hover .file-card-quick-actions {\n opacity: 1;\n}\n\n.quick-action-btn {\n width: 22px;\n height: 22px;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 0.68rem;\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.quick-action-btn:hover {\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n/* Card Icon / Thumbnail */\n.file-card-icon-container {\n height: 74px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-md, 8px);\n margin-bottom: 8px;\n font-size: 2rem;\n transition: transform 0.15s ease;\n}\n\n.file-card:hover .file-card-icon-container {\n transform: scale(1.04);\n}\n\n/* Card Body */\n.file-card-body {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.file-card-name {\n font-size: 0.8rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n line-height: 1.25;\n min-height: 2em;\n}\n\n.file-card-meta {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n/* ==========================================================================\n List View Table\n ========================================================================== */\n\n.file-table-container {\n flex: 1;\n overflow-y: auto;\n padding: 8px 16px;\n}\n\n.file-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.82rem;\n}\n\n.file-table th {\n text-align: left;\n padding: 8px 10px;\n border-bottom: 1px solid var(--mj-border-default);\n color: var(--mj-text-muted);\n font-size: 0.72rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n user-select: none;\n cursor: pointer;\n}\n\n.file-table th.th-check {\n width: 32px;\n cursor: default;\n}\n\n.file-table th.th-actions {\n width: 70px;\n text-align: right;\n cursor: default;\n}\n\n.sort-arrow {\n margin-left: 4px;\n font-size: 0.65rem;\n}\n\n.file-table-row {\n border-bottom: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: background-color 0.12s ease;\n}\n\n.file-table-row:hover {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 4%, var(--mj-bg-surface));\n}\n\n.file-table-row.selected {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.file-table-row td {\n padding: 8px 10px;\n vertical-align: middle;\n}\n\n.row-name-wrap {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 0;\n}\n\n.row-icon-badge {\n width: 28px;\n height: 28px;\n min-width: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-sm, 6px);\n font-size: 0.9rem;\n}\n\n.row-file-name {\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.type-badge {\n display: inline-block;\n padding: 2px 6px;\n border-radius: 4px;\n background-color: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n font-size: 0.7rem;\n}\n\n.size-text, .date-text {\n color: var(--mj-text-secondary);\n font-size: 0.76rem;\n white-space: nowrap;\n}\n\n.row-action-buttons {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 4px;\n opacity: 0;\n transition: opacity 0.12s ease;\n}\n\n.file-table-row:hover .row-action-buttons {\n opacity: 1;\n}\n\n.row-act-btn {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 0.7rem;\n cursor: pointer;\n}\n\n.row-act-btn:hover {\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n/* ==========================================================================\n Media Color Badges\n ========================================================================== */\n\n.mj-file-thumb--folder {\n background-color: color-mix(in srgb, var(--mj-status-warning, #f59e0b) 14%, transparent);\n color: var(--mj-status-warning, #f59e0b);\n}\n\n.mj-file-thumb--pdf {\n background-color: color-mix(in srgb, var(--mj-status-error, #ef4444) 14%, transparent);\n color: var(--mj-status-error, #ef4444);\n}\n\n.mj-file-thumb--image {\n background-color: color-mix(in srgb, var(--mj-media-image, #8b5cf6) 14%, transparent);\n color: var(--mj-media-image, #8b5cf6);\n}\n\n.mj-file-thumb--video {\n background-color: color-mix(in srgb, var(--mj-brand-primary, #3b82f6) 14%, transparent);\n color: var(--mj-brand-primary, #3b82f6);\n}\n\n.mj-file-thumb--audio {\n background-color: color-mix(in srgb, var(--mj-status-warning, #f59e0b) 14%, transparent);\n color: var(--mj-status-warning, #f59e0b);\n}\n\n.mj-file-thumb--code {\n background-color: color-mix(in srgb, var(--mj-status-success, #10b981) 14%, transparent);\n color: var(--mj-status-success, #10b981);\n}\n\n.mj-file-thumb--text {\n background-color: color-mix(in srgb, var(--mj-text-muted, #6b7280) 14%, transparent);\n color: var(--mj-text-muted, #6b7280);\n}\n\n.mj-file-thumb--document {\n background-color: color-mix(in srgb, var(--mj-brand-primary-hover, #2563eb) 14%, transparent);\n color: var(--mj-brand-primary-hover, #2563eb);\n}\n\n/* ==========================================================================\n Status Bar (Bottom Information)\n ========================================================================== */\n\n.status-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 16px;\n background-color: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 3%, var(--mj-bg-surface)));\n border-top: 1px solid var(--mj-border-default);\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n flex-shrink: 0;\n}\n\n.status-account-badge {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--mj-brand-primary);\n font-weight: 500;\n}\n\n/* ==========================================================================\n Drag & Drop Overlay & Progress\n ========================================================================== */\n\n.drag-drop-overlay {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, rgba(255, 255, 255, 0.9));\n backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 100;\n pointer-events: none;\n}\n\n.drag-drop-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 32px 48px;\n background-color: var(--mj-bg-surface);\n border: 2px dashed var(--mj-brand-primary);\n border-radius: var(--mj-radius-lg, 12px);\n box-shadow: var(--mj-shadow-lg, 0 10px 25px rgba(0, 0, 0, 0.15));\n}\n\n.drag-icon {\n font-size: 3rem;\n color: var(--mj-brand-primary);\n margin-bottom: 12px;\n}\n\n.drag-drop-content h3 {\n margin: 0 0 6px 0;\n font-size: 1.1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.drag-drop-content p {\n margin: 0;\n font-size: 0.8rem;\n color: var(--mj-text-secondary);\n}\n\n.upload-progress-bar-wrap {\n position: absolute;\n bottom: 36px;\n right: 20px;\n z-index: 90;\n}\n\n.upload-progress-card {\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n padding: 10px 14px;\n box-shadow: var(--mj-shadow-lg, 0 8px 20px rgba(0, 0, 0, 0.15));\n width: 260px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.upload-info {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.upload-spinner {\n color: var(--mj-brand-primary);\n}\n\n.upload-text {\n font-size: 0.78rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.progress-track {\n height: 4px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 9999px;\n overflow: hidden;\n}\n\n.progress-fill {\n height: 100%;\n background-color: var(--mj-brand-primary);\n transition: width 0.15s ease;\n}\n\n.grid-loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1;\n}\n\n/* ==========================================================================\n Modals (Design Tokens & Glass Backdrop)\n ========================================================================== */\n\n.mj-modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1050;\n padding: 20px;\n animation: modalFadeIn 0.18s ease-out;\n}\n\n@keyframes modalFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.mj-modal-window {\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n box-shadow: var(--mj-shadow-xl, 0 20px 35px rgba(0, 0, 0, 0.25));\n width: 100%;\n max-width: 500px;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.mj-modal-window.modal-compact {\n max-width: 420px;\n}\n\n.mj-modal-window.preview-modal-window {\n max-width: 850px;\n max-height: 85vh;\n height: 80vh;\n}\n\n.mj-modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 18px;\n border-bottom: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface);\n}\n\n.modal-title-wrap {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.modal-title {\n margin: 0;\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.modal-subtitle {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.modal-header-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.modal-close-btn {\n background: transparent;\n border: none;\n color: var(--mj-text-secondary);\n font-size: 1rem;\n cursor: pointer;\n padding: 4px;\n border-radius: 4px;\n}\n\n.modal-close-btn:hover {\n color: var(--mj-text-primary);\n background-color: var(--mj-bg-surface-sunken);\n}\n\n.mj-modal-body {\n padding: 18px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.form-label {\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.form-input {\n padding: 8px 12px;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.85rem;\n outline: none;\n}\n\n.form-input:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.modal-body-text {\n margin: 0;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n}\n\n.modal-warning-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background-color: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n border: 1px solid var(--mj-status-warning);\n border-radius: var(--mj-radius-sm, 6px);\n font-size: 0.76rem;\n color: var(--mj-text-primary);\n}\n\n.mj-modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 18px;\n border-top: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface-sunken);\n}\n\n/* Preview Modal Body */\n.preview-modal-body {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: var(--mj-bg-surface-sunken);\n padding: 16px;\n overflow: hidden;\n}\n\n.preview-media-wrap {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.preview-img {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n border-radius: var(--mj-radius-md, 8px);\n}\n\n.preview-iframe {\n width: 100%;\n height: 100%;\n border: none;\n border-radius: var(--mj-radius-md, 8px);\n}\n\n.preview-video {\n max-width: 100%;\n max-height: 100%;\n border-radius: var(--mj-radius-md, 8px);\n}\n\n.audio-preview-wrap {\n flex-direction: column;\n gap: 16px;\n}\n\n.preview-audio-icon {\n font-size: 3rem;\n color: var(--mj-brand-primary);\n}\n\n.preview-audio {\n width: 320px;\n}\n\n.preview-fallback {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n text-align: center;\n color: var(--mj-text-secondary);\n}\n\n.preview-fallback-icon {\n font-size: 3rem;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n}\n\n.preview-fallback h4 {\n margin: 0;\n font-size: 0.95rem;\n color: var(--mj-text-primary);\n}\n\n.preview-fallback p {\n margin: 0;\n font-size: 0.8rem;\n}\n\n.account-picker-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n max-height: 180px;\n overflow-y: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n padding: 6px;\n}\n\n.account-picker-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.account-picker-item:hover {\n background-color: var(--mj-bg-surface-sunken);\n}\n\n.picker-name {\n font-size: 0.82rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.picker-badge {\n margin-left: auto;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n.copy-progress-indicator {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 0.78rem;\n color: var(--mj-brand-primary);\n margin-top: 8px;\n}\n"] }]
2749
- }], () => [], { account: [{
3384
+ args: [{ standalone: false, selector: 'mj-file-grid', template: "<div class=\"file-grid-container\"\n (dragenter)=\"onDragEnter($event)\"\n (dragover)=\"onDragOver($event)\"\n (dragleave)=\"onDragLeave($event)\"\n (drop)=\"onDrop($event)\">\n\n <!-- Top Command Bar: Breadcrumbs & Primary Actions -->\n @if (!isLoading && !errorMessage) {\n <div class=\"command-bar breadcrumb-bar\">\n <!-- Left: Breadcrumb Navigation Trail -->\n <div class=\"command-bar-left\">\n <div class=\"nav-btn-group nav-buttons\">\n <button\n type=\"button\"\n class=\"mj-tool-btn nav-btn\"\n [disabled]=\"!canNavigateUp()\"\n (click)=\"navigateUp()\"\n title=\"Go up to parent folder\">\n <i class=\"fa-solid fa-arrow-up\"></i>\n </button>\n <button\n type=\"button\"\n class=\"mj-tool-btn nav-btn\"\n (click)=\"refresh()\"\n title=\"Refresh current folder\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n </button>\n </div>\n\n <nav class=\"breadcrumb-trail breadcrumb-path\" aria-label=\"Folder Path\">\n <i class=\"fa-solid fa-folder-open trail-root-icon\"></i>\n @for (crumb of getPathBreadcrumbs(); track crumb.path; let last = $last) {\n <button\n type=\"button\"\n class=\"crumb-btn\"\n [class.current]=\"last\"\n (click)=\"navigateToBreadcrumb(crumb.path)\"\n [title]=\"crumb.name\">\n {{ crumb.name }}\n </button>\n @if (!last) {\n <span class=\"crumb-separator\">/</span>\n }\n }\n </nav>\n </div>\n\n <!-- Right: Search, Filter, View Mode & Action Buttons -->\n <div class=\"command-bar-right\">\n <!-- Search & Filter Bar -->\n <div class=\"search-pill search-bar\">\n <i class=\"fa-solid fa-magnifying-glass search-icon\"></i>\n <input\n type=\"text\"\n class=\"search-input\"\n [(ngModel)]=\"searchQuery\"\n (ngModelChange)=\"onSearchChange($event)\"\n placeholder=\"Search files...\"\n [disabled]=\"isLoading || isMultiProviderSearchMode\" />\n @if (searchQuery && !isMultiProviderSearchMode) {\n <button\n type=\"button\"\n class=\"search-clear-btn\"\n (click)=\"clearSearch()\"\n title=\"Clear search\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n }\n <select\n class=\"type-filter-select file-type-filter\"\n [(ngModel)]=\"fileTypeFilter\"\n (ngModelChange)=\"onFileTypeFilterChange($event)\"\n [disabled]=\"isLoading || isMultiProviderSearchMode\">\n <option value=\"all\">All Items</option>\n <option value=\"files\">Files only</option>\n <option value=\"folders\">Folders only</option>\n </select>\n </div>\n\n <!-- Search Across Accounts Toggle -->\n <button\n type=\"button\"\n class=\"mj-btn-search-toggle multi-search-btn\"\n [class.active]=\"isMultiProviderSearchMode\"\n (click)=\"toggleMultiAccountSearchMode()\"\n title=\"Search across all storage accounts\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <span>All Accounts</span>\n </button>\n\n <!-- View Switcher -->\n <div class=\"view-mode-toggle view-toggle\">\n <button\n type=\"button\"\n class=\"view-mode-btn view-btn\"\n [class.active]=\"viewMode === 'list'\"\n (click)=\"viewMode = 'list'\"\n title=\"List View\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n <button\n type=\"button\"\n class=\"view-mode-btn view-btn\"\n [class.active]=\"viewMode === 'grid'\"\n (click)=\"viewMode = 'grid'\"\n title=\"Grid View\">\n <i class=\"fa-solid fa-table-cells-large\"></i>\n </button>\n </div>\n\n <!-- Primary Action CTAs -->\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary toolbar-btn\"\n (click)=\"onNewFolderClick()\"\n title=\"Create New Folder\">\n <i class=\"fa-solid fa-folder-plus\"></i>\n <span>New Folder</span>\n </button>\n\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary toolbar-btn\"\n (click)=\"onUploadClick()\"\n title=\"Upload Files\">\n <i class=\"fa-solid fa-cloud-arrow-up\"></i>\n <span>Upload</span>\n </button>\n </div>\n </div>\n }\n\n <!-- Selection Action Bar (Appears when 1+ items selected) -->\n @if (selectedItems.length > 0 && !isMultiProviderSearchMode) {\n <div class=\"selection-bar\">\n <div class=\"selection-bar-left\">\n <label class=\"selection-checkbox-wrap\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"areAllSelected()\"\n (change)=\"areAllSelected() ? deselectAll() : selectAll()\" />\n <span class=\"selection-count\">{{ selectedItems.length }} item{{ selectedItems.length === 1 ? '' : 's' }} selected</span>\n </label>\n </div>\n\n <div class=\"selection-bar-right\">\n @if (selectedItems.length === 1 && getSelectedItem()?.Type === 'file') {\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"openPreview(getSelectedItem()!)\"\n title=\"Preview file\">\n <i class=\"fa-solid fa-eye\"></i>\n <span>Preview</span>\n </button>\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"onDownloadClick()\"\n title=\"Download file\">\n <i class=\"fa-solid fa-download\"></i>\n <span>Download</span>\n </button>\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"onCopyToAccountClick()\"\n title=\"Copy to another storage account\">\n <i class=\"fa-solid fa-cloud-arrow-up\"></i>\n <span>Copy to...</span>\n </button>\n }\n @if (selectedItems.length === 1) {\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"onRenameClick()\"\n title=\"Rename item\">\n <i class=\"fa-solid fa-pen\"></i>\n <span>Rename</span>\n </button>\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"onCopyClick()\"\n title=\"Copy to path\">\n <i class=\"fa-solid fa-copy\"></i>\n <span>Copy</span>\n </button>\n <button\n type=\"button\"\n class=\"sel-action-btn\"\n (click)=\"onMoveClick()\"\n title=\"Move item\">\n <i class=\"fa-solid fa-arrows-up-down-left-right\"></i>\n <span>Move</span>\n </button>\n }\n <button\n type=\"button\"\n class=\"sel-action-btn sel-action-btn--danger\"\n (click)=\"onDeleteClick()\"\n title=\"Delete selected item(s)\">\n <i class=\"fa-solid fa-trash\"></i>\n <span>Delete</span>\n </button>\n <button\n type=\"button\"\n class=\"sel-action-btn sel-action-btn--ghost\"\n (click)=\"deselectAll()\"\n title=\"Deselect all\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n </div>\n }\n\n <!-- Multi-Provider Search Panel -->\n @if (isMultiProviderSearchMode) {\n <div class=\"multi-search-card multi-provider-search-panel\">\n <div class=\"multi-search-header search-panel-header\">\n <div class=\"multi-search-title\">\n <i class=\"fa-solid fa-layer-group\"></i>\n <h3>Search Across Storage Accounts</h3>\n </div>\n <button\n type=\"button\"\n class=\"modal-close-btn close-panel-btn\"\n (click)=\"toggleMultiAccountSearchMode()\"\n title=\"Close search\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n\n <div class=\"multi-search-body search-panel-body\">\n <div class=\"multi-search-bar search-input-row\">\n <input\n type=\"text\"\n class=\"multi-search-text multi-search-input\"\n [(ngModel)]=\"multiProviderSearchQuery\"\n placeholder=\"Type query to search across connected accounts...\"\n (keydown.enter)=\"executeMultiAccountSearch()\"\n [disabled]=\"isSearching\" />\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary search-execute-btn\"\n (click)=\"executeMultiAccountSearch()\"\n [disabled]=\"isSearching || !multiProviderSearchQuery.trim() || selectedSearchProviders.size === 0\">\n @if (isSearching) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else {\n <i class=\"fa-solid fa-magnifying-glass\"></i>\n }\n <span>{{ isSearching ? 'Searching...' : 'Search Accounts' }}</span>\n </button>\n </div>\n\n <div class=\"multi-search-accounts provider-selection\">\n <span class=\"account-select-label provider-selection-label\">Target Accounts:</span>\n <div class=\"account-pill-list provider-checkboxes\">\n @for (a of availableAccounts; track a.account.ID) {\n <label\n class=\"account-select-pill provider-checkbox\"\n [class.disabled]=\"!accountSupportsSearch(a)\"\n [class.checked]=\"isAccountSelectedForSearch(a.account.ID)\">\n <input\n type=\"checkbox\"\n [checked]=\"isAccountSelectedForSearch(a.account.ID)\"\n (change)=\"toggleSearchAccount(a.account.ID)\"\n [disabled]=\"!accountSupportsSearch(a)\" />\n <span class=\"pill-account-name provider-name\">{{ a.account.Name }}</span>\n <span class=\"pill-provider-tag provider-type\">{{ a.provider.Name }}</span>\n @if (!accountSupportsSearch(a)) {\n <span class=\"pill-no-search no-search-badge\">(no search)</span>\n }\n </label>\n }\n </div>\n </div>\n </div>\n\n <!-- Search Results -->\n @if (multiProviderSearchResults) {\n <div class=\"multi-search-results search-results-container\">\n <div class=\"results-meta-bar search-results-summary\">\n <span class=\"meta-count result-count\">\n <strong>{{ multiProviderSearchResults.TotalResultsReturned }}</strong> result{{ multiProviderSearchResults.TotalResultsReturned === 1 ? '' : 's' }} found\n </span>\n <span class=\"meta-stats provider-stats\">\n ({{ multiProviderSearchResults.SuccessfulAccounts }} account{{ multiProviderSearchResults.SuccessfulAccounts === 1 ? '' : 's' }} searched)\n </span>\n <button\n type=\"button\"\n class=\"clear-results-btn\"\n (click)=\"clearMultiProviderSearch()\">\n <i class=\"fa-solid fa-xmark\"></i> Clear Results\n </button>\n </div>\n\n <div class=\"results-groups provider-results-list\">\n @for (accountResult of multiProviderSearchResults.AccountResults; track accountResult.accountID) {\n <div class=\"result-group provider-result-group\" [class.failed]=\"!accountResult.success\">\n <div class=\"result-group-head provider-result-header\">\n <i class=\"fa-solid fa-cloud-bolt group-icon\"></i>\n <span class=\"group-account-name provider-name\">{{ accountResult.accountName }}</span>\n @if (accountResult.success) {\n <span class=\"group-count-badge result-count\">{{ accountResult.results.length }}</span>\n } @else {\n <span class=\"group-error-badge error-badge\">{{ accountResult.errorMessage }}</span>\n }\n </div>\n\n @if (accountResult.success && accountResult.results.length > 0) {\n <div class=\"result-items-grid provider-result-files\">\n @for (file of accountResult.results; track file.path) {\n <div class=\"search-item-row search-result-item\">\n <i [class]=\"getSearchResultIcon(file)\" class=\"search-item-icon file-icon\"></i>\n <div class=\"search-item-info file-info\">\n <span class=\"search-item-name file-name\">{{ file.name }}</span>\n <span class=\"search-item-path file-path\">{{ file.path }}</span>\n </div>\n <span class=\"search-item-size file-size\">{{ formatSearchResultSize(file.size) }}</span>\n <span class=\"search-item-date file-date\">{{ formatSearchResultDate(file.lastModified) }}</span>\n </div>\n }\n </div>\n }\n @if (accountResult.success && accountResult.results.length === 0) {\n <div class=\"group-empty-text no-results-message\">No matching files found in this account</div>\n }\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Loading State -->\n @if (isLoading && !isMultiProviderSearchMode) {\n <div class=\"grid-loading-container loading-state\">\n <mj-loading text=\"Loading files...\"></mj-loading>\n </div>\n }\n\n <!-- Error State -->\n @if (errorMessage && !isLoading) {\n <mj-empty-state\n class=\"file-grid-empty-fill\"\n Variant=\"error\"\n Icon=\"fa-solid fa-triangle-exclamation\"\n Title=\"Unable to load files\"\n [Message]=\"errorMessage\"\n ActionText=\"Retry\"\n ActionIcon=\"fa-solid fa-arrows-rotate\"\n (Action)=\"refresh()\" />\n }\n\n <!-- Grid View -->\n @if (!isLoading && !errorMessage && filteredItems.length > 0 && viewMode === 'grid') {\n <div class=\"file-cards-grid file-grid file-grid-wrapper\">\n @for (item of filteredItems; track item.Key) {\n <div\n class=\"file-card grid-item\"\n [class.selected]=\"selectedItems.includes(item.Key)\"\n (click)=\"onTileClick(item, $event)\"\n (dblclick)=\"onItemDoubleClick(item)\"\n [title]=\"item.name\">\n\n <!-- Card Top Row: Checkbox & Quick Actions -->\n <div class=\"file-card-header\">\n <label class=\"file-card-checkbox\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedItems.includes(item.Key)\"\n (change)=\"toggleItemSelection(item, $event)\" />\n </label>\n <div class=\"file-card-quick-actions\" (click)=\"$event.stopPropagation()\">\n @if (item.Type === 'file') {\n <button\n type=\"button\"\n class=\"quick-action-btn\"\n (click)=\"openPreview(item)\"\n title=\"Preview\">\n <i class=\"fa-solid fa-eye\"></i>\n </button>\n <button\n type=\"button\"\n class=\"quick-action-btn\"\n (click)=\"downloadFile(item)\"\n title=\"Download\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n }\n </div>\n </div>\n\n <!-- Card Thumbnail/Icon -->\n <div class=\"file-card-icon-container grid-icon\" [class]=\"getItemColorClass(item)\">\n <i [class]=\"getItemIcon(item)\"></i>\n </div>\n\n <!-- Card Body: Name & Meta -->\n <div class=\"file-card-body\">\n <span class=\"file-card-name grid-name\">{{ item.name }}</span>\n <div class=\"file-card-meta grid-meta\">\n <span class=\"meta-size\">{{ item.Type === 'folder' ? 'Folder' : formatFileSize(item.Size) }}</span>\n <span class=\"meta-date\">{{ formatDate(item.LastModified) }}</span>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- List View Table -->\n @if (!isLoading && !errorMessage && filteredItems.length > 0 && viewMode === 'list') {\n <div class=\"file-table-container file-table-wrapper\">\n <table class=\"file-table\">\n <thead>\n <tr>\n <th class=\"th-check\">\n <input\n type=\"checkbox\"\n [checked]=\"areAllSelected()\"\n (change)=\"areAllSelected() ? deselectAll() : selectAll()\" />\n </th>\n <th class=\"th-name col-name\" (click)=\"onSortChange([{ field: 'name', dir: sort[0].field === 'name' && sort[0].dir === 'asc' ? 'desc' : 'asc' }])\">\n <span>Name</span>\n <i class=\"fa-solid sort-arrow sort-icon\"\n [class.fa-caret-up]=\"sort[0].field === 'name' && sort[0].dir === 'asc'\"\n [class.fa-caret-down]=\"sort[0].field === 'name' && sort[0].dir === 'desc'\"\n [class.fa-sort]=\"sort[0].field !== 'name'\"></i>\n </th>\n <th class=\"th-type col-type\">Type</th>\n <th class=\"th-size col-size\" (click)=\"onSortChange([{ field: 'size', dir: sort[0].field === 'size' && sort[0].dir === 'asc' ? 'desc' : 'asc' }])\">\n <span>Size</span>\n <i class=\"fa-solid sort-arrow sort-icon\"\n [class.fa-caret-up]=\"sort[0].field === 'size' && sort[0].dir === 'asc'\"\n [class.fa-caret-down]=\"sort[0].field === 'size' && sort[0].dir === 'desc'\"\n [class.fa-sort]=\"sort[0].field !== 'size'\"></i>\n </th>\n <th class=\"th-date col-modified\" (click)=\"onSortChange([{ field: 'lastModified', dir: sort[0].field === 'lastModified' && sort[0].dir === 'asc' ? 'desc' : 'asc' }])\">\n <span>Modified</span>\n <i class=\"fa-solid sort-arrow sort-icon\"\n [class.fa-caret-up]=\"sort[0].field === 'lastModified' && sort[0].dir === 'asc'\"\n [class.fa-caret-down]=\"sort[0].field === 'lastModified' && sort[0].dir === 'desc'\"\n [class.fa-sort]=\"sort[0].field !== 'lastModified'\"></i>\n </th>\n <th class=\"th-actions\">Actions</th>\n </tr>\n </thead>\n <tbody>\n @for (item of filteredItems; track item.Key) {\n <tr\n class=\"file-table-row file-row\"\n [class.selected]=\"selectedItems.includes(item.Key)\"\n (click)=\"onTileClick(item, $event)\"\n (dblclick)=\"onItemDoubleClick(item)\">\n <td class=\"td-check\" (click)=\"$event.stopPropagation()\">\n <input\n type=\"checkbox\"\n [checked]=\"selectedItems.includes(item.Key)\"\n (change)=\"toggleItemSelection(item, $event)\" />\n </td>\n <td class=\"td-name col-name\">\n <div class=\"row-name-wrap\">\n <div class=\"row-icon-badge\" [class]=\"getItemColorClass(item)\">\n <i [class]=\"getItemIcon(item)\" class=\"item-icon\"></i>\n </div>\n <span class=\"row-file-name item-name\" [title]=\"item.name\">{{ item.name }}</span>\n </div>\n </td>\n <td class=\"td-type col-type\">\n <span class=\"type-badge item-type\">{{ getFileType(item) }}</span>\n </td>\n <td class=\"td-size col-size\">\n <span class=\"size-text item-size\">{{ item.Type === 'folder' ? '\u2014' : formatFileSize(item.Size) }}</span>\n </td>\n <td class=\"td-date col-modified\">\n <span class=\"date-text item-date\">{{ formatDate(item.LastModified) }}</span>\n </td>\n <td class=\"td-actions\" (click)=\"$event.stopPropagation()\">\n <div class=\"row-action-buttons\">\n @if (item.Type === 'file') {\n <button\n type=\"button\"\n class=\"row-act-btn\"\n (click)=\"openPreview(item)\"\n title=\"Preview\">\n <i class=\"fa-solid fa-eye\"></i>\n </button>\n <button\n type=\"button\"\n class=\"row-act-btn\"\n (click)=\"downloadFile(item)\"\n title=\"Download\">\n <i class=\"fa-solid fa-download\"></i>\n </button>\n }\n </div>\n </td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n\n <!-- Empty State -->\n @if (!isLoading && !errorMessage && filteredItems.length === 0 && !isMultiProviderSearchMode) {\n <mj-empty-state\n Icon=\"fa-solid fa-folder-open\"\n Title=\"This folder is empty\"\n Message=\"Drop files here to upload or create a new folder\"\n ActionText=\"Upload Files\"\n ActionIcon=\"fa-solid fa-cloud-arrow-up\"\n (Action)=\"onUploadClick()\" />\n }\n\n <!-- Bottom Status Bar -->\n @if (!isLoading && !errorMessage) {\n <div class=\"status-bar\">\n <span class=\"status-count\">\n {{ filteredItems.length }} item{{ filteredItems.length === 1 ? '' : 's' }}\n @if (searchQuery) {\n (filtered)\n }\n </span>\n <div class=\"status-account-badge\" *ngIf=\"account\">\n <i class=\"fa-solid fa-cloud\"></i>\n <span>{{ account.account.Name }} ({{ account.provider.Name }})</span>\n </div>\n </div>\n }\n\n <!-- Drag & Drop Upload Overlay -->\n @if (isDragging) {\n <div class=\"drag-drop-overlay\">\n <div class=\"drag-drop-content\">\n <i class=\"fa-solid fa-cloud-arrow-up drag-icon\"></i>\n <h3>Drop files here to upload</h3>\n <p>Files will be stored in {{ folderPath || 'Root' }}</p>\n </div>\n </div>\n }\n\n <!-- Upload Progress Bar -->\n @if (isUploading) {\n <div class=\"upload-progress-bar-wrap\">\n <div class=\"upload-progress-card\">\n <div class=\"upload-info\">\n <i class=\"fa-solid fa-spinner fa-spin upload-spinner\"></i>\n <span class=\"upload-text\">Uploading {{ uploadingFileName }}...</span>\n </div>\n <div class=\"progress-track\">\n <div class=\"progress-fill\" [style.width.%]=\"uploadProgress\"></div>\n </div>\n </div>\n </div>\n }\n</div>\n\n<!-- ==========================================================================\n MODAL DIALOGS (Design Tokens, Smooth Animations, Backdrop Blurs)\n ========================================================================== -->\n\n<!-- File Preview Modal -->\n@if (showPreviewModal && previewItem) {\n <div class=\"mj-modal-backdrop\" (click)=\"closePreview()\">\n <div class=\"mj-modal-window preview-modal-window\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header\">\n <div class=\"modal-title-wrap\">\n <div class=\"row-icon-badge\" [class]=\"getItemColorClass(previewItem)\">\n <i [class]=\"getItemIcon(previewItem)\"></i>\n </div>\n <div>\n <h3 class=\"modal-title\">{{ previewItem.name }}</h3>\n <span class=\"modal-subtitle\">{{ formatFileSize(previewItem.Size) }} &bull; {{ formatDate(previewItem.LastModified) }}</span>\n </div>\n </div>\n <div class=\"modal-header-actions\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"downloadFile(previewItem)\"\n title=\"Download file\">\n <i class=\"fa-solid fa-download\"></i>\n <span>Download</span>\n </button>\n <button\n type=\"button\"\n class=\"modal-close-btn\"\n (click)=\"closePreview()\"\n title=\"Close preview\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n </div>\n\n <div class=\"preview-modal-body\">\n @if (isLoadingPreview) {\n <div class=\"preview-loading\">\n <mj-loading text=\"Loading preview...\"></mj-loading>\n </div>\n } @else if (previewUrl) {\n @switch (previewMediaType) {\n @case ('image') {\n <div class=\"preview-media-wrap image-preview-wrap\">\n <img [src]=\"previewUrl\" [alt]=\"previewItem.name\" class=\"preview-img\" />\n </div>\n }\n @case ('pdf') {\n <div class=\"preview-media-wrap pdf-preview-wrap\">\n <iframe [src]=\"previewUrl\" class=\"preview-iframe\" title=\"PDF Preview\"></iframe>\n </div>\n }\n @case ('video') {\n <div class=\"preview-media-wrap video-preview-wrap\">\n <video [src]=\"previewUrl\" controls autoplay class=\"preview-video\"></video>\n </div>\n }\n @case ('audio') {\n <div class=\"preview-media-wrap audio-preview-wrap\">\n <i class=\"fa-solid fa-music preview-audio-icon\"></i>\n <audio [src]=\"previewUrl\" controls class=\"preview-audio\"></audio>\n </div>\n }\n @default {\n <div class=\"preview-fallback\">\n <i [class]=\"getItemIcon(previewItem)\" class=\"preview-fallback-icon\"></i>\n <h4>{{ previewItem.name }}</h4>\n <p>Preview is not supported for this file type.</p>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"downloadFile(previewItem)\">\n <i class=\"fa-solid fa-download\"></i>\n <span>Download File</span>\n </button>\n </div>\n }\n }\n } @else {\n <div class=\"preview-fallback\">\n <i class=\"fa-solid fa-triangle-exclamation preview-fallback-icon\"></i>\n <h4>Preview unavailable</h4>\n <p>Could not generate a preview link for this file.</p>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"downloadFile(previewItem)\">\n <i class=\"fa-solid fa-download\"></i>\n <span>Download File</span>\n </button>\n </div>\n }\n </div>\n </div>\n </div>\n}\n\n<!-- New Folder Modal -->\n@if (showNewFolderDialog) {\n <div class=\"mj-modal-backdrop modal-overlay\" (click)=\"onCancelNewFolder()\">\n <div class=\"mj-modal-window modal-compact modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header modal-header\">\n <h3 class=\"modal-title\">Create New Folder</h3>\n <button type=\"button\" class=\"modal-close-btn modal-close\" (click)=\"onCancelNewFolder()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body modal-body\">\n <label class=\"form-label\">Folder name:</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"newFolderName\"\n [disabled]=\"isCreatingFolder\"\n placeholder=\"Enter folder name\"\n (keydown.enter)=\"onCreateFolder()\"\n autofocus />\n </div>\n <div class=\"mj-modal-footer modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelNewFolder()\"\n [disabled]=\"isCreatingFolder\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"onCreateFolder()\"\n [disabled]=\"!newFolderName.trim() || isCreatingFolder\">\n @if (isCreatingFolder) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isCreatingFolder ? 'Creating...' : 'Create' }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n\n<!-- Delete Confirmation Modal -->\n@if (showDeleteDialog && itemToDelete) {\n <div class=\"mj-modal-backdrop modal-overlay\" (click)=\"onCancelDelete()\">\n <div class=\"mj-modal-window modal-compact modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header modal-header\">\n <h3 class=\"modal-title\">Delete {{ itemToDelete.Type === 'folder' ? 'Folder' : 'File' }}?</h3>\n <button type=\"button\" class=\"modal-close-btn modal-close\" (click)=\"onCancelDelete()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body modal-body\">\n <p class=\"modal-body-text\">\n Are you sure you want to delete <strong>{{ itemToDelete.name }}</strong>?\n </p>\n @if (itemToDelete.Type === 'folder') {\n <div class=\"modal-warning-banner warning\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i>\n <span>This will delete the folder and all its contents.</span>\n </div>\n }\n </div>\n <div class=\"mj-modal-footer modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelDelete()\"\n [disabled]=\"isDeleting\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--danger danger\"\n (click)=\"onConfirmDelete()\"\n [disabled]=\"isDeleting\">\n @if (isDeleting) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isDeleting ? 'Deleting...' : 'Delete' }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n\n<!-- Rename Modal -->\n@if (showRenameDialog && itemToRename) {\n <div class=\"mj-modal-backdrop modal-overlay\" (click)=\"onCancelRename()\">\n <div class=\"mj-modal-window modal-compact modal-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header modal-header\">\n <h3 class=\"modal-title\">Rename {{ itemToRename.Type === 'folder' ? 'Folder' : 'File' }}</h3>\n <button type=\"button\" class=\"modal-close-btn modal-close\" (click)=\"onCancelRename()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body modal-body\">\n <label class=\"form-label\">New name:</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"newItemName\"\n [disabled]=\"isRenaming\"\n placeholder=\"Enter new name\"\n (keydown.enter)=\"onConfirmRename()\"\n autofocus />\n </div>\n <div class=\"mj-modal-footer modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelRename()\"\n [disabled]=\"isRenaming\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"onConfirmRename()\"\n [disabled]=\"isRenaming || !newItemName.trim()\">\n @if (isRenaming) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isRenaming ? 'Renaming...' : 'Rename' }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n\n<!-- Copy Modal -->\n@if (showCopyDialog && itemToCopy) {\n <div class=\"mj-modal-backdrop\" (click)=\"onCancelCopy()\">\n <div class=\"mj-modal-window modal-compact\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header\">\n <h3 class=\"modal-title\">Copy {{ itemToCopy.Type === 'folder' ? 'Folder' : 'File' }}</h3>\n <button type=\"button\" class=\"modal-close-btn\" (click)=\"onCancelCopy()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body\">\n <p class=\"modal-body-text\">Copying <strong>{{ itemToCopy.name }}</strong></p>\n <label class=\"form-label\">Destination Path</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"copyDestinationPath\"\n [disabled]=\"isCopying\"\n placeholder=\"path/to/destination\"\n (keydown.enter)=\"onConfirmCopy()\" />\n </div>\n <div class=\"mj-modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelCopy()\"\n [disabled]=\"isCopying\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"onConfirmCopy()\"\n [disabled]=\"isCopying || !copyDestinationPath.trim()\">\n @if (isCopying) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isCopying ? 'Copying...' : 'Copy' }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n\n<!-- Move Modal -->\n@if (showMoveDialog && itemToMove) {\n <div class=\"mj-modal-backdrop\" (click)=\"onCancelMove()\">\n <div class=\"mj-modal-window modal-compact\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header\">\n <h3 class=\"modal-title\">Move {{ itemToMove.Type === 'folder' ? 'Folder' : 'File' }}</h3>\n <button type=\"button\" class=\"modal-close-btn\" (click)=\"onCancelMove()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body\">\n <p class=\"modal-body-text\">Moving <strong>{{ itemToMove.name }}</strong></p>\n <label class=\"form-label\">Destination Folder</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"moveDestinationPath\"\n [disabled]=\"isMoving\"\n placeholder=\"destination/folder/\"\n (keydown.enter)=\"onConfirmMove()\" />\n </div>\n <div class=\"mj-modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelMove()\"\n [disabled]=\"isMoving\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"onConfirmMove()\"\n [disabled]=\"isMoving || !moveDestinationPath.trim()\">\n @if (isMoving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isMoving ? 'Moving...' : 'Move' }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n\n<!-- Copy to Account Modal -->\n@if (showCopyToProviderDialog && itemToCopyToProvider) {\n <div class=\"mj-modal-backdrop\" (click)=\"onCancelCopyToAccount()\">\n <div class=\"mj-modal-window\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-modal-header\">\n <h3 class=\"modal-title\">Copy to Another Account</h3>\n <button type=\"button\" class=\"modal-close-btn\" (click)=\"onCancelCopyToAccount()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n <div class=\"mj-modal-body\">\n <p class=\"modal-body-text\">\n Copying <strong>{{ itemToCopyToProvider.name }}</strong> from <strong>{{ account?.account?.Name }}</strong>\n </p>\n\n <label class=\"form-label\">Select Destination Storage Accounts</label>\n <div class=\"account-picker-list\">\n @for (a of availableAccounts; track a.account.ID) {\n <label class=\"account-picker-item\" [for]=\"'dest-acc-' + a.account.ID\">\n <input\n type=\"checkbox\"\n [id]=\"'dest-acc-' + a.account.ID\"\n [checked]=\"isDestinationAccountSelected(a.account.ID)\"\n [disabled]=\"isCopyingToAccount\"\n (change)=\"toggleDestinationAccount(a.account.ID)\" />\n <span class=\"picker-name\">{{ a.account.Name }}</span>\n <span class=\"picker-badge\">{{ a.provider.Name }}</span>\n </label>\n }\n </div>\n\n <label class=\"form-label\" style=\"margin-top: 14px;\">Destination Filename</label>\n <input\n type=\"text\"\n class=\"form-input\"\n [(ngModel)]=\"copyToAccountDestinationPath\"\n [disabled]=\"isCopyingToAccount\"\n placeholder=\"Filename\"\n (keydown.enter)=\"onConfirmCopyToAccount()\" />\n\n @if (copyToAccountProgress) {\n <div class=\"copy-progress-indicator\">\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n <span>Copying to {{ copyToAccountProgress.currentAccount }} ({{ copyToAccountProgress.current }}/{{ copyToAccountProgress.total }})...</span>\n </div>\n }\n </div>\n <div class=\"mj-modal-footer\">\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--secondary\"\n (click)=\"onCancelCopyToAccount()\"\n [disabled]=\"isCopyingToAccount\">\n Cancel\n </button>\n <button\n type=\"button\"\n class=\"mj-action-btn mj-action-btn--primary\"\n (click)=\"onConfirmCopyToAccount()\"\n [disabled]=\"isCopyingToAccount || selectedDestinationAccounts.size === 0 || !copyToAccountDestinationPath.trim()\">\n @if (isCopyingToAccount) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n }\n <span>{{ isCopyingToAccount ? 'Copying...' : 'Copy to ' + selectedDestinationAccounts.size + ' Account' + (selectedDestinationAccounts.size !== 1 ? 's' : '') }}</span>\n </button>\n </div>\n </div>\n </div>\n}\n", styles: ["/* ==========================================================================\n File Grid & Table Component Styles\n Modern macOS Finder / Cloud Storage with Design Tokens\n ========================================================================== */\n\n:host {\n display: block;\n width: 100%;\n height: 100%;\n overflow: hidden;\n}\n\n.file-grid-container {\n display: flex;\n flex-direction: column;\n height: 100%;\n width: 100%;\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n position: relative;\n overflow: hidden;\n}\n\n/* ==========================================================================\n Command Bar (Top Navigation, Search & Primary Actions)\n ========================================================================== */\n\n.command-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n padding: 10px 16px;\n background-color: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n flex-wrap: wrap;\n}\n\n.command-bar-left {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n flex: 1;\n}\n\n.nav-btn-group {\n display: flex;\n align-items: center;\n gap: 4px;\n flex-shrink: 0;\n}\n\n.mj-tool-btn {\n width: 32px;\n height: 32px;\n min-width: 32px;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mj-tool-btn:hover:not([disabled]) {\n background-color: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.mj-tool-btn[disabled] {\n opacity: 0.4;\n cursor: not-allowed;\n background-color: transparent;\n}\n\n/* Breadcrumb Trail */\n.breadcrumb-trail {\n display: flex;\n align-items: center;\n gap: 4px;\n overflow-x: auto;\n overflow-y: hidden;\n scrollbar-width: none;\n min-width: 0;\n padding: 2px 4px;\n}\n\n.breadcrumb-trail::-webkit-scrollbar {\n display: none;\n}\n\n.trail-root-icon {\n color: var(--mj-status-warning, #f59e0b);\n font-size: 0.95rem;\n margin-right: 2px;\n flex-shrink: 0;\n}\n\n.crumb-btn {\n background: transparent;\n border: none;\n color: var(--mj-text-secondary);\n font-size: 0.82rem;\n font-weight: 500;\n padding: 4px 8px;\n border-radius: var(--mj-radius-sm, 6px);\n cursor: pointer;\n white-space: nowrap;\n transition: all 0.12s ease;\n}\n\n.crumb-btn:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n}\n\n.crumb-btn.current {\n color: var(--mj-brand-primary);\n font-weight: 600;\n background-color: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.crumb-separator {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n}\n\n/* Command Bar Right */\n.command-bar-right {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n\n/* Search Pill */\n.search-pill {\n display: flex;\n align-items: center;\n background-color: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 4%, var(--mj-bg-surface)));\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-full, 9999px);\n padding: 2px 10px;\n gap: 6px;\n transition: border-color 0.15s ease;\n}\n\n.search-pill:focus-within {\n border-color: var(--mj-brand-primary);\n}\n\n.search-icon {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.search-input {\n border: none;\n background: transparent;\n outline: none;\n font-size: 0.82rem;\n color: var(--mj-text-primary);\n width: 150px;\n padding: 4px 0;\n}\n\n.search-input::placeholder {\n color: var(--mj-text-muted);\n}\n\n.search-clear-btn {\n background: transparent;\n border: none;\n color: var(--mj-text-muted);\n cursor: pointer;\n padding: 0 4px;\n font-size: 0.75rem;\n}\n\n.search-clear-btn:hover {\n color: var(--mj-text-primary);\n}\n\n.type-filter-select {\n border: none;\n background: transparent;\n font-size: 0.76rem;\n color: var(--mj-text-secondary);\n outline: none;\n cursor: pointer;\n padding: 2px 4px;\n border-left: 1px solid var(--mj-border-default);\n}\n\n/* Toggle Multi-Search Button */\n.mj-btn-search-toggle {\n display: flex;\n align-items: center;\n gap: 6px;\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-secondary);\n font-size: 0.8rem;\n font-weight: 500;\n padding: 6px 12px;\n border-radius: var(--mj-radius-full, 9999px);\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.mj-btn-search-toggle:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n color: var(--mj-text-primary);\n}\n\n.mj-btn-search-toggle.active {\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n border-color: var(--mj-brand-primary);\n}\n\n/* View Mode Toggle */\n.view-mode-toggle {\n display: flex;\n background-color: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 4%, var(--mj-bg-surface)));\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n padding: 2px;\n gap: 2px;\n}\n\n.view-mode-btn {\n background: transparent;\n border: none;\n color: var(--mj-text-secondary);\n padding: 5px 8px;\n font-size: 0.8rem;\n border-radius: 4px;\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.view-mode-btn:hover {\n color: var(--mj-text-primary);\n}\n\n.view-mode-btn.active {\n background-color: var(--mj-bg-surface);\n color: var(--mj-brand-primary);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);\n}\n\n/* Action Buttons */\n.mj-action-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n font-size: 0.82rem;\n font-weight: 500;\n border-radius: var(--mj-radius-sm, 6px);\n cursor: pointer;\n transition: all 0.15s ease;\n border: 1px solid transparent;\n white-space: nowrap;\n}\n\n.mj-action-btn--primary {\n background-color: var(--mj-brand-primary, #3b82f6);\n color: var(--mj-text-inverse, #ffffff);\n border-color: var(--mj-brand-primary, #3b82f6);\n}\n\n.mj-action-btn--primary:hover:not([disabled]) {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 85%, var(--mj-text-primary, #000000));\n}\n\n.mj-action-btn--secondary {\n background-color: var(--mj-bg-surface);\n border-color: var(--mj-border-default);\n color: var(--mj-text-primary);\n}\n\n.mj-action-btn--secondary:hover:not([disabled]) {\n background-color: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n}\n\n.mj-action-btn--danger {\n background-color: var(--mj-status-error, #ef4444);\n color: var(--mj-text-inverse, #ffffff);\n border-color: var(--mj-status-error, #ef4444);\n}\n\n.mj-action-btn--danger:hover:not([disabled]) {\n background-color: color-mix(in srgb, var(--mj-status-error, #ef4444) 85%, var(--mj-text-primary, #000000));\n}\n\n.mj-action-btn[disabled] {\n opacity: 0.5;\n cursor: not-allowed;\n}\n\n/* ==========================================================================\n Selection Action Bar\n ========================================================================== */\n\n.selection-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 16px;\n background-color: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border-bottom: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n gap: 12px;\n flex-shrink: 0;\n animation: slideDown 0.18s ease-out;\n}\n\n@keyframes slideDown {\n from { transform: translateY(-100%); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}\n\n.selection-bar-left {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.selection-checkbox-wrap {\n display: flex;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n user-select: none;\n}\n\n.selection-count {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-brand-primary);\n}\n\n.selection-bar-right {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.sel-action-btn {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 4px 10px;\n font-size: 0.76rem;\n font-weight: 500;\n border-radius: var(--mj-radius-sm, 6px);\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n color: var(--mj-text-primary);\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.sel-action-btn:hover {\n background-color: color-mix(in srgb, var(--mj-text-primary) 6%, var(--mj-bg-surface));\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.sel-action-btn--danger:hover {\n background-color: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n.sel-action-btn--ghost {\n background: transparent;\n border-color: transparent;\n}\n\n/* ==========================================================================\n Multi-Account Search Card\n ========================================================================== */\n\n.multi-search-card {\n margin: 12px 16px;\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 10px);\n box-shadow: var(--mj-shadow-md, 0 4px 12px rgba(0, 0, 0, 0.08));\n overflow: hidden;\n animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(-6px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.multi-search-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background-color: var(--mj-bg-surface-sunken);\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.multi-search-title {\n display: flex;\n align-items: center;\n gap: 8px;\n color: var(--mj-brand-primary);\n}\n\n.multi-search-title h3 {\n margin: 0;\n font-size: 0.88rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.multi-search-body {\n padding: 14px 16px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.multi-search-bar {\n display: flex;\n gap: 8px;\n}\n\n.multi-search-text {\n flex: 1;\n padding: 8px 12px;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.85rem;\n outline: none;\n}\n\n.multi-search-text:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.multi-search-accounts {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.account-select-label {\n font-size: 0.74rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n}\n\n.account-pill-list {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.account-select-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n border-radius: var(--mj-radius-full, 9999px);\n border: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface-sunken);\n font-size: 0.76rem;\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.account-select-pill.checked {\n border-color: var(--mj-brand-primary);\n background-color: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.account-select-pill.disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.pill-account-name {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.pill-provider-tag {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n}\n\n.pill-no-search {\n font-size: 0.68rem;\n color: var(--mj-status-warning);\n}\n\n/* Results */\n.multi-search-results {\n border-top: 1px solid var(--mj-border-default);\n padding: 12px 16px;\n max-height: 400px;\n overflow-y: auto;\n}\n\n.results-meta-bar {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 12px;\n font-size: 0.8rem;\n}\n\n.meta-count {\n color: var(--mj-text-primary);\n}\n\n.meta-stats {\n color: var(--mj-text-secondary);\n}\n\n.clear-results-btn {\n margin-left: auto;\n background: transparent;\n border: none;\n color: var(--mj-text-secondary);\n font-size: 0.76rem;\n cursor: pointer;\n}\n\n.clear-results-btn:hover {\n color: var(--mj-text-primary);\n}\n\n.results-groups {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.result-group {\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n overflow: hidden;\n}\n\n.result-group-head {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background-color: var(--mj-bg-surface-sunken);\n font-size: 0.8rem;\n}\n\n.group-account-name {\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.group-count-badge {\n margin-left: auto;\n background-color: var(--mj-brand-primary);\n color: var(--mj-text-inverse, #ffffff);\n font-size: 0.7rem;\n font-weight: 600;\n padding: 1px 6px;\n border-radius: 9999px;\n}\n\n.group-error-badge {\n margin-left: auto;\n color: var(--mj-status-error);\n font-size: 0.72rem;\n}\n\n.result-items-grid {\n display: flex;\n flex-direction: column;\n}\n\n.search-item-row {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n font-size: 0.8rem;\n}\n\n.search-item-row:last-child {\n border-bottom: none;\n}\n\n.search-item-icon {\n color: var(--mj-brand-primary);\n}\n\n.search-item-info {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n}\n\n.search-item-name {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.search-item-path {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n.search-item-size, .search-item-date {\n font-size: 0.74rem;\n color: var(--mj-text-secondary);\n}\n\n.group-empty-text {\n padding: 12px;\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n text-align: center;\n}\n\n/* ==========================================================================\n Cards Grid View\n ========================================================================== */\n\n.file-cards-grid {\n flex: 1;\n overflow-y: auto;\n padding: 16px;\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));\n gap: 14px;\n align-content: start;\n}\n\n.file-card {\n display: flex;\n flex-direction: column;\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 10px);\n padding: 10px;\n cursor: pointer;\n transition: all 0.15s cubic-bezier(0.4, 0, 0.2, 1);\n position: relative;\n user-select: none;\n}\n\n.file-card:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-md, 0 4px 12px rgba(0, 0, 0, 0.08));\n transform: translateY(-2px);\n}\n\n.file-card.selected {\n border-color: var(--mj-brand-primary);\n background-color: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface));\n box-shadow: 0 0 0 1px var(--mj-brand-primary);\n}\n\n.file-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n height: 22px;\n margin-bottom: 6px;\n}\n\n.file-card-checkbox {\n cursor: pointer;\n}\n\n.file-card-quick-actions {\n display: flex;\n gap: 4px;\n opacity: 0;\n transition: opacity 0.15s ease;\n}\n\n.file-card:hover .file-card-quick-actions {\n opacity: 1;\n}\n\n.quick-action-btn {\n width: 22px;\n height: 22px;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 0.68rem;\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.quick-action-btn:hover {\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n/* Card Icon / Thumbnail */\n.file-card-icon-container {\n height: 74px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-md, 8px);\n margin-bottom: 8px;\n font-size: 2rem;\n transition: transform 0.15s ease;\n}\n\n.file-card:hover .file-card-icon-container {\n transform: scale(1.04);\n}\n\n/* Card Body */\n.file-card-body {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.file-card-name {\n font-size: 0.8rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n line-height: 1.25;\n min-height: 2em;\n}\n\n.file-card-meta {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n/* ==========================================================================\n List View Table\n ========================================================================== */\n\n.file-table-container {\n flex: 1;\n overflow-y: auto;\n padding: 8px 16px;\n}\n\n.file-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 0.82rem;\n}\n\n.file-table th {\n text-align: left;\n padding: 8px 10px;\n border-bottom: 1px solid var(--mj-border-default);\n color: var(--mj-text-muted);\n font-size: 0.72rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n user-select: none;\n cursor: pointer;\n}\n\n.file-table th.th-check {\n width: 32px;\n cursor: default;\n}\n\n.file-table th.th-actions {\n width: 70px;\n text-align: right;\n cursor: default;\n}\n\n.sort-arrow {\n margin-left: 4px;\n font-size: 0.65rem;\n}\n\n.file-table-row {\n border-bottom: 1px solid var(--mj-border-default);\n cursor: pointer;\n transition: background-color 0.12s ease;\n}\n\n.file-table-row:hover {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 4%, var(--mj-bg-surface));\n}\n\n.file-table-row.selected {\n background-color: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.file-table-row td {\n padding: 8px 10px;\n vertical-align: middle;\n}\n\n.row-name-wrap {\n display: flex;\n align-items: center;\n gap: 10px;\n min-width: 0;\n}\n\n.row-icon-badge {\n width: 28px;\n height: 28px;\n min-width: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--mj-radius-sm, 6px);\n font-size: 0.9rem;\n}\n\n.row-file-name {\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.type-badge {\n display: inline-block;\n padding: 2px 6px;\n border-radius: 4px;\n background-color: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n font-size: 0.7rem;\n}\n\n.size-text, .date-text {\n color: var(--mj-text-secondary);\n font-size: 0.76rem;\n white-space: nowrap;\n}\n\n.row-action-buttons {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 4px;\n opacity: 0;\n transition: opacity 0.12s ease;\n}\n\n.file-table-row:hover .row-action-buttons {\n opacity: 1;\n}\n\n.row-act-btn {\n width: 24px;\n height: 24px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n color: var(--mj-text-secondary);\n font-size: 0.7rem;\n cursor: pointer;\n}\n\n.row-act-btn:hover {\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n/* ==========================================================================\n Media Color Badges\n ========================================================================== */\n\n.mj-file-thumb--folder {\n background-color: color-mix(in srgb, var(--mj-status-warning, #f59e0b) 14%, transparent);\n color: var(--mj-status-warning, #f59e0b);\n}\n\n.mj-file-thumb--pdf {\n background-color: color-mix(in srgb, var(--mj-status-error, #ef4444) 14%, transparent);\n color: var(--mj-status-error, #ef4444);\n}\n\n.mj-file-thumb--image {\n background-color: color-mix(in srgb, var(--mj-media-image, #8b5cf6) 14%, transparent);\n color: var(--mj-media-image, #8b5cf6);\n}\n\n.mj-file-thumb--video {\n background-color: color-mix(in srgb, var(--mj-brand-primary, #3b82f6) 14%, transparent);\n color: var(--mj-brand-primary, #3b82f6);\n}\n\n.mj-file-thumb--audio {\n background-color: color-mix(in srgb, var(--mj-status-warning, #f59e0b) 14%, transparent);\n color: var(--mj-status-warning, #f59e0b);\n}\n\n.mj-file-thumb--code {\n background-color: color-mix(in srgb, var(--mj-status-success, #10b981) 14%, transparent);\n color: var(--mj-status-success, #10b981);\n}\n\n.mj-file-thumb--text {\n background-color: color-mix(in srgb, var(--mj-text-muted, #6b7280) 14%, transparent);\n color: var(--mj-text-muted, #6b7280);\n}\n\n.mj-file-thumb--document {\n background-color: color-mix(in srgb, var(--mj-brand-primary-hover, #2563eb) 14%, transparent);\n color: var(--mj-brand-primary-hover, #2563eb);\n}\n\n/* ==========================================================================\n Status Bar (Bottom Information)\n ========================================================================== */\n\n.status-bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 16px;\n background-color: var(--mj-bg-surface-sunken, color-mix(in srgb, var(--mj-text-primary) 3%, var(--mj-bg-surface)));\n border-top: 1px solid var(--mj-border-default);\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n flex-shrink: 0;\n}\n\n.status-account-badge {\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--mj-brand-primary);\n font-weight: 500;\n}\n\n/* ==========================================================================\n Drag & Drop Overlay & Progress\n ========================================================================== */\n\n.drag-drop-overlay {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, rgba(255, 255, 255, 0.9));\n backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 100;\n pointer-events: none;\n}\n\n.drag-drop-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 32px 48px;\n background-color: var(--mj-bg-surface);\n border: 2px dashed var(--mj-brand-primary);\n border-radius: var(--mj-radius-lg, 12px);\n box-shadow: var(--mj-shadow-lg, 0 10px 25px rgba(0, 0, 0, 0.15));\n}\n\n.drag-icon {\n font-size: 3rem;\n color: var(--mj-brand-primary);\n margin-bottom: 12px;\n}\n\n.drag-drop-content h3 {\n margin: 0 0 6px 0;\n font-size: 1.1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.drag-drop-content p {\n margin: 0;\n font-size: 0.8rem;\n color: var(--mj-text-secondary);\n}\n\n.upload-progress-bar-wrap {\n position: absolute;\n bottom: 36px;\n right: 20px;\n z-index: 90;\n}\n\n.upload-progress-card {\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md, 8px);\n padding: 10px 14px;\n box-shadow: var(--mj-shadow-lg, 0 8px 20px rgba(0, 0, 0, 0.15));\n width: 260px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.upload-info {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.upload-spinner {\n color: var(--mj-brand-primary);\n}\n\n.upload-text {\n font-size: 0.78rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.progress-track {\n height: 4px;\n background-color: var(--mj-bg-surface-sunken);\n border-radius: 9999px;\n overflow: hidden;\n}\n\n.progress-fill {\n height: 100%;\n background-color: var(--mj-brand-primary);\n transition: width 0.15s ease;\n}\n\n.grid-loading-container {\n display: flex;\n align-items: center;\n justify-content: center;\n flex: 1;\n}\n\n/* ==========================================================================\n Modals (Design Tokens & Glass Backdrop)\n ========================================================================== */\n\n.mj-modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n backdrop-filter: blur(4px);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1050;\n padding: 20px;\n animation: modalFadeIn 0.18s ease-out;\n}\n\n@keyframes modalFadeIn {\n from { opacity: 0; }\n to { opacity: 1; }\n}\n\n.mj-modal-window {\n background-color: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n box-shadow: var(--mj-shadow-xl, 0 20px 35px rgba(0, 0, 0, 0.25));\n width: 100%;\n max-width: 500px;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n}\n\n.mj-modal-window.modal-compact {\n max-width: 420px;\n}\n\n.mj-modal-window.preview-modal-window {\n max-width: 850px;\n max-height: 85vh;\n height: 80vh;\n}\n\n.mj-modal-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 18px;\n border-bottom: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface);\n}\n\n.modal-title-wrap {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.modal-title {\n margin: 0;\n font-size: 0.95rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.modal-subtitle {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.modal-header-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.modal-close-btn {\n background: transparent;\n border: none;\n color: var(--mj-text-secondary);\n font-size: 1rem;\n cursor: pointer;\n padding: 4px;\n border-radius: 4px;\n}\n\n.modal-close-btn:hover {\n color: var(--mj-text-primary);\n background-color: var(--mj-bg-surface-sunken);\n}\n\n.mj-modal-body {\n padding: 18px;\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.form-label {\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.form-input {\n padding: 8px 12px;\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.85rem;\n outline: none;\n}\n\n.form-input:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.modal-body-text {\n margin: 0;\n font-size: 0.85rem;\n color: var(--mj-text-primary);\n}\n\n.modal-warning-banner {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background-color: color-mix(in srgb, var(--mj-status-warning) 12%, var(--mj-bg-surface));\n border: 1px solid var(--mj-status-warning);\n border-radius: var(--mj-radius-sm, 6px);\n font-size: 0.76rem;\n color: var(--mj-text-primary);\n}\n\n.mj-modal-footer {\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 18px;\n border-top: 1px solid var(--mj-border-default);\n background-color: var(--mj-bg-surface-sunken);\n}\n\n/* Preview Modal Body */\n.preview-modal-body {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n background-color: var(--mj-bg-surface-sunken);\n padding: 16px;\n overflow: hidden;\n}\n\n.preview-media-wrap {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.preview-img {\n max-width: 100%;\n max-height: 100%;\n object-fit: contain;\n border-radius: var(--mj-radius-md, 8px);\n}\n\n.preview-iframe {\n width: 100%;\n height: 100%;\n border: none;\n border-radius: var(--mj-radius-md, 8px);\n}\n\n.preview-video {\n max-width: 100%;\n max-height: 100%;\n border-radius: var(--mj-radius-md, 8px);\n}\n\n.audio-preview-wrap {\n flex-direction: column;\n gap: 16px;\n}\n\n.preview-audio-icon {\n font-size: 3rem;\n color: var(--mj-brand-primary);\n}\n\n.preview-audio {\n width: 320px;\n}\n\n.preview-fallback {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n text-align: center;\n color: var(--mj-text-secondary);\n}\n\n.preview-fallback-icon {\n font-size: 3rem;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n}\n\n.preview-fallback h4 {\n margin: 0;\n font-size: 0.95rem;\n color: var(--mj-text-primary);\n}\n\n.preview-fallback p {\n margin: 0;\n font-size: 0.8rem;\n}\n\n.account-picker-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n max-height: 180px;\n overflow-y: auto;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm, 6px);\n padding: 6px;\n}\n\n.account-picker-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 6px 8px;\n border-radius: 4px;\n cursor: pointer;\n}\n\n.account-picker-item:hover {\n background-color: var(--mj-bg-surface-sunken);\n}\n\n.picker-name {\n font-size: 0.82rem;\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.picker-badge {\n margin-left: auto;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n.copy-progress-indicator {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 0.78rem;\n color: var(--mj-brand-primary);\n margin-top: 8px;\n}\n"] }]
3385
+ }], () => [], { Account: [{
3386
+ type: Input
3387
+ }], account: [{
3388
+ type: Input
3389
+ }], FolderPath: [{
2750
3390
  type: Input
2751
3391
  }], folderPath: [{
2752
3392
  type: Input
3393
+ }], FolderNavigate: [{
3394
+ type: Output
2753
3395
  }], folderNavigate: [{
2754
3396
  type: Output
3397
+ }], FolderStructureChanged: [{
3398
+ type: Output
2755
3399
  }], folderStructureChanged: [{
2756
3400
  type: Output
2757
3401
  }] }); })();