@@ -1385,16 +1385,35 @@ Parent ID: ${item.parent_id || 'N/A'}
1385
1385
const parentMap = new Map ( ) ;
1386
1386
const rootItems = [ ] ;
1387
1387
1388
- // Create virtual folders for directory structure
1389
- files . forEach ( file => {
1388
+ // Separate files and folders
1389
+ const actualFiles = files . filter ( item => item . type === 'file' ) ;
1390
+ const actualFolders = files . filter ( item => item . type === 'folder' ) ;
1391
+
1392
+ // First, add all actual folders to the maps
1393
+ actualFolders . forEach ( folder => {
1394
+ const normalizedPath = folder . relative_path . replace ( / \\ / g, '/' ) ;
1395
+ if ( ! itemsMap . has ( normalizedPath ) ) {
1396
+ const folderObj = {
1397
+ ...folder ,
1398
+ id : `folder_${ folder . path } ` ,
1399
+ name : folder . name ,
1400
+ type : 'folder' ,
1401
+ path : normalizedPath ,
1402
+ isVirtual : false
1403
+ } ;
1404
+ itemsMap . set ( normalizedPath , folderObj ) ;
1405
+ }
1406
+ } ) ;
1407
+
1408
+ // Create virtual folders for any missing parent directories of files
1409
+ actualFiles . forEach ( file => {
1390
1410
// Handle both Windows (\) and Unix (/) path separators
1391
1411
const pathParts = file . relative_path . replace ( / \\ / g, '/' ) . split ( '/' ) . filter ( part => part . length > 0 ) ;
1392
1412
1393
- // Create all parent directories as virtual folders
1413
+ // Create all parent directories as virtual folders (only if they don't already exist as actual folders)
1394
1414
let currentPath = '' ;
1395
1415
for ( let i = 0 ; i < pathParts . length - 1 ; i ++ ) {
1396
1416
const part = pathParts [ i ] ;
1397
- const parentPath = currentPath ;
1398
1417
currentPath = currentPath ? `${ currentPath } /${ part } ` : part ;
1399
1418
1400
1419
if ( ! itemsMap . has ( currentPath ) ) {
@@ -1406,19 +1425,29 @@ Parent ID: ${item.parent_id || 'N/A'}
1406
1425
isVirtual : true
1407
1426
} ;
1408
1427
itemsMap . set ( currentPath , virtualFolder ) ;
1428
+ }
1429
+ }
1430
+ } ) ;
1409
1431
1410
- if ( parentPath ) {
1411
- if ( ! parentMap . has ( parentPath ) ) {
1412
- parentMap . set ( parentPath , [ ] ) ;
1413
- }
1414
- parentMap . get ( parentPath ) . push ( virtualFolder ) ;
1415
- } else {
1416
- rootItems . push ( virtualFolder ) ;
1417
- }
1432
+ // Now build the hierarchy relationships
1433
+ itemsMap . forEach ( ( item , path ) => {
1434
+ const pathParts = path . split ( '/' ) ;
1435
+ if ( pathParts . length === 1 ) {
1436
+ // Root level item
1437
+ rootItems . push ( item ) ;
1438
+ } else {
1439
+ // Find parent
1440
+ const parentPath = pathParts . slice ( 0 , - 1 ) . join ( '/' ) ;
1441
+ if ( ! parentMap . has ( parentPath ) ) {
1442
+ parentMap . set ( parentPath , [ ] ) ;
1418
1443
}
1444
+ parentMap . get ( parentPath ) . push ( item ) ;
1419
1445
}
1446
+ } ) ;
1420
1447
1421
- // Add the file itself
1448
+ // Add files to their parent directories
1449
+ actualFiles . forEach ( file => {
1450
+ const pathParts = file . relative_path . replace ( / \\ / g, '/' ) . split ( '/' ) . filter ( part => part . length > 0 ) ;
1422
1451
const fileName = pathParts [ pathParts . length - 1 ] ;
1423
1452
const fileObj = {
1424
1453
...file ,
@@ -1447,19 +1476,44 @@ Parent ID: ${item.parent_id || 'N/A'}
1447
1476
const itemElement = this . createUploadTreeItem ( item , level ) ;
1448
1477
container . appendChild ( itemElement ) ;
1449
1478
1450
- // Add children if this is a folder
1451
- if ( item . type === 'folder' && parentMap . has ( item . path ) ) {
1479
+ // Add children if this is a folder and it has children
1480
+ if ( item . type === 'folder' ) {
1481
+ const children = parentMap . get ( item . path ) || [ ] ;
1452
1482
const childrenContainer = document . createElement ( 'div' ) ;
1453
1483
childrenContainer . className = 'tree-children' ;
1454
- childrenContainer . id = `upload-children-${ item . id } ` ;
1484
+ // Create a safe ID by replacing problematic characters
1485
+ const safeId = item . id . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, '_' ) ;
1486
+ childrenContainer . id = `upload-children-${ safeId } ` ;
1455
1487
childrenContainer . style . display = 'block' ; // Start expanded like download tree
1456
1488
1457
- this . createUploadTreeElements (
1458
- parentMap . get ( item . path ) ,
1459
- childrenContainer ,
1460
- parentMap ,
1461
- level + 1
1462
- ) ;
1489
+ if ( children . length > 0 ) {
1490
+ this . createUploadTreeElements (
1491
+ children ,
1492
+ childrenContainer ,
1493
+ parentMap ,
1494
+ level + 1
1495
+ ) ;
1496
+ } else {
1497
+ // Handle empty folders - add a placeholder or just leave empty
1498
+ // The folder will still be expandable but show as empty
1499
+ const emptyMessage = document . createElement ( 'div' ) ;
1500
+ emptyMessage . className = 'tree-item empty-folder level-' + ( level + 1 ) ;
1501
+ emptyMessage . innerHTML = `
1502
+ <div class="tree-item-content upload-compact" style="opacity: 0.6; font-style: italic;">
1503
+ <span style="margin-left: 20px;">Empty folder</span>
1504
+ </div>
1505
+ ` ;
1506
+ childrenContainer . appendChild ( emptyMessage ) ;
1507
+ }
1508
+
1509
+ // Update the toggle button class based on whether folder has children
1510
+ const toggleButton = itemElement . querySelector ( '.tree-toggle' ) ;
1511
+ if ( toggleButton ) {
1512
+ if ( children . length === 0 ) {
1513
+ toggleButton . classList . remove ( 'expanded' ) ;
1514
+ toggleButton . classList . add ( 'leaf' ) ;
1515
+ }
1516
+ }
1463
1517
1464
1518
container . appendChild ( childrenContainer ) ;
1465
1519
}
@@ -1478,7 +1532,7 @@ Parent ID: ${item.parent_id || 'N/A'}
1478
1532
if ( item . type === 'folder' ) {
1479
1533
// For folders: "Folder Name (folder)" - we'll add count later if needed
1480
1534
content = `
1481
- <button class="tree-toggle expanded" onclick="window.synapseApp.toggleUploadFolder(' ${ item . id } ') ">
1535
+ <button class="tree-toggle expanded" data-folder-id=" ${ this . escapeHtml ( item . id ) } ">
1482
1536
</button>
1483
1537
<div class="tree-item-content upload-compact">
1484
1538
<i class="fas fa-folder tree-item-icon folder"></i>
@@ -1492,20 +1546,32 @@ Parent ID: ${item.parent_id || 'N/A'}
1492
1546
<button class="tree-toggle leaf">
1493
1547
</button>
1494
1548
<div class="tree-item-content upload-compact">
1495
- <input type="checkbox" class="item-checkbox" data-id="${ item . id } ">
1549
+ <input type="checkbox" class="item-checkbox" data-id="${ this . escapeHtml ( item . id ) } ">
1496
1550
<i class="fas fa-file tree-item-icon file"></i>
1497
1551
<span class="upload-item-info">${ this . escapeHtml ( item . name ) } <span class="item-meta">(${ sizeStr } )</span></span>
1498
1552
</div>
1499
1553
` ;
1554
+ }
1500
1555
1556
+ itemDiv . innerHTML = content ;
1557
+
1558
+ // Add event listeners instead of inline onclick handlers
1559
+ if ( item . type === 'folder' ) {
1560
+ const toggleButton = itemDiv . querySelector ( '.tree-toggle' ) ;
1561
+ if ( toggleButton ) {
1562
+ toggleButton . addEventListener ( 'click' , ( ) => {
1563
+ this . toggleUploadFolder ( item . id ) ;
1564
+ } ) ;
1565
+ }
1566
+ } else {
1567
+ // Add event listener for file checkbox
1501
1568
itemDiv . addEventListener ( 'change' , ( e ) => {
1502
1569
if ( e . target . classList . contains ( 'item-checkbox' ) ) {
1503
1570
this . handleUploadFileSelection ( item . id , e . target . checked ) ;
1504
1571
}
1505
1572
} ) ;
1506
1573
}
1507
1574
1508
- itemDiv . innerHTML = content ;
1509
1575
return itemDiv ;
1510
1576
}
1511
1577
@@ -1545,8 +1611,17 @@ Parent ID: ${item.parent_id || 'N/A'}
1545
1611
}
1546
1612
1547
1613
toggleUploadFolder ( itemId ) {
1548
- const toggleButton = document . querySelector ( `#upload-files-tree .tree-item[data-id="${ itemId } "] .tree-toggle` ) ;
1549
- const childrenDiv = document . getElementById ( `upload-children-${ itemId } ` ) ;
1614
+ // Find the tree item and toggle button using the dataset id
1615
+ const treeItem = document . querySelector ( `#upload-files-tree .tree-item[data-id="${ CSS . escape ( itemId ) } "]` ) ;
1616
+ if ( ! treeItem ) {
1617
+ console . warn ( 'Could not find tree item for ID:' , itemId ) ;
1618
+ return ;
1619
+ }
1620
+
1621
+ const toggleButton = treeItem . querySelector ( '.tree-toggle' ) ;
1622
+ // Create the same safe ID used when creating the container
1623
+ const safeId = itemId . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, '_' ) ;
1624
+ const childrenDiv = document . getElementById ( `upload-children-${ safeId } ` ) ;
1550
1625
1551
1626
if ( toggleButton && childrenDiv ) {
1552
1627
const isExpanded = toggleButton . classList . contains ( 'expanded' ) ;
@@ -1560,6 +1635,8 @@ Parent ID: ${item.parent_id || 'N/A'}
1560
1635
toggleButton . classList . add ( 'expanded' ) ;
1561
1636
childrenDiv . style . display = 'block' ;
1562
1637
}
1638
+ } else {
1639
+ console . warn ( 'Could not find toggle button or children container for ID:' , itemId , 'Safe ID:' , safeId ) ;
1563
1640
}
1564
1641
}
1565
1642
@@ -1569,7 +1646,8 @@ Parent ID: ${item.parent_id || 'N/A'}
1569
1646
if ( ! toggle . classList . contains ( 'leaf' ) ) {
1570
1647
const treeItem = toggle . closest ( '.tree-item' ) ;
1571
1648
const itemId = treeItem . dataset . id ;
1572
- const childrenContainer = document . getElementById ( `upload-children-${ itemId } ` ) ;
1649
+ const safeId = itemId . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, '_' ) ;
1650
+ const childrenContainer = document . getElementById ( `upload-children-${ safeId } ` ) ;
1573
1651
if ( childrenContainer ) {
1574
1652
childrenContainer . style . display = 'block' ;
1575
1653
toggle . classList . remove ( 'collapsed' ) ;
@@ -1585,7 +1663,8 @@ Parent ID: ${item.parent_id || 'N/A'}
1585
1663
if ( ! toggle . classList . contains ( 'leaf' ) ) {
1586
1664
const treeItem = toggle . closest ( '.tree-item' ) ;
1587
1665
const itemId = treeItem . dataset . id ;
1588
- const childrenContainer = document . getElementById ( `upload-children-${ itemId } ` ) ;
1666
+ const safeId = itemId . replace ( / [ ^ a - z A - Z 0 - 9 _ - ] / g, '_' ) ;
1667
+ const childrenContainer = document . getElementById ( `upload-children-${ safeId } ` ) ;
1589
1668
if ( childrenContainer ) {
1590
1669
childrenContainer . style . display = 'none' ;
1591
1670
toggle . classList . remove ( 'expanded' ) ;
@@ -1595,18 +1674,6 @@ Parent ID: ${item.parent_id || 'N/A'}
1595
1674
} ) ;
1596
1675
}
1597
1676
1598
- collapseAllUploadFolders ( ) {
1599
- const toggles = document . querySelectorAll ( '#upload-files-tree .tree-toggle' ) ;
1600
- toggles . forEach ( toggle => {
1601
- const folderElement = toggle . closest ( '.tree-item' ) ;
1602
- const childrenContainer = folderElement . nextElementSibling ;
1603
- if ( childrenContainer && childrenContainer . classList . contains ( 'tree-children' ) ) {
1604
- childrenContainer . style . display = 'none' ;
1605
- toggle . textContent = '▶' ;
1606
- }
1607
- } ) ;
1608
- }
1609
-
1610
1677
formatFileSize ( bytes ) {
1611
1678
if ( ! bytes ) return '' ;
1612
1679
const sizes = [ 'B' , 'KB' , 'MB' , 'GB' , 'TB' ] ;
@@ -1629,10 +1696,11 @@ Parent ID: ${item.parent_id || 'N/A'}
1629
1696
}
1630
1697
1631
1698
try {
1632
- // Get selected file data
1633
- const selectedFileData = this . uploadFileItems . filter ( file =>
1634
- this . selectedUploadFiles . has ( `file_${ file . path } ` )
1635
- ) ;
1699
+ // Get selected file data - handle both old and new ID formats
1700
+ const selectedFileData = this . uploadFileItems . filter ( file => {
1701
+ const fileId = `file_${ file . path } ` ;
1702
+ return this . selectedUploadFiles . has ( fileId ) || this . selectedUploadFiles . has ( file . id ) ;
1703
+ } ) ;
1636
1704
1637
1705
const result = await window . electronAPI . bulkUpload ( {
1638
1706
parent_id : parentId ,
0 commit comments