@@ -3146,26 +3146,32 @@ async function fetchFolderPeek(folder, sourceId = '') {
31463146// Wire "select all" header checkbox for the current table render
31473147function wireSelectAll ( fileListContent ) {
31483148 // Be flexible about how the header checkbox is identified
3149- const selectAll = fileListContent . querySelector (
3149+ const getSelectAll = ( ) => fileListContent . querySelector (
31503150 'thead input[type="checkbox"].select-all, ' +
31513151 'thead .select-all input[type="checkbox"], ' +
31523152 'thead input#selectAll, ' +
31533153 'thead input#selectAllCheckbox, ' +
31543154 'thead input[data-select-all]'
31553155 ) ;
3156+ const selectAll = getSelectAll ( ) ;
31563157 if ( ! selectAll ) return ;
3158+ if ( selectAll . __wiredSelectAll ) return ;
3159+ selectAll . __wiredSelectAll = true ;
31573160
31583161 const getRowCbs = ( ) =>
31593162 Array . from ( fileListContent . querySelectorAll ( 'tbody .file-checkbox' ) )
3160- . filter ( cb => ! cb . disabled ) ;
3163+ . filter ( cb => ! cb . disabled && ! cb . closest ( 'tr.folder-row' ) ) ;
31613164
3162- // Toggle all rows when the header checkbox changes
3163- selectAll . addEventListener ( 'change' , ( ) => {
3164- // Clear any folder selection when toggling all files
3165- document . querySelectorAll ( '#fileList .folder-checkbox:checked' ) . forEach ( cb => {
3165+ const clearFolderSelections = ( ) => {
3166+ fileListContent . querySelectorAll ( 'tbody .folder-checkbox:checked' ) . forEach ( cb => {
31663167 cb . checked = false ;
31673168 updateRowHighlight ( cb ) ;
31683169 } ) ;
3170+ } ;
3171+
3172+ const applySelectAll = ( ) => {
3173+ // Clear any folder selection when toggling all files
3174+ clearFolderSelections ( ) ;
31693175 const checked = selectAll . checked ;
31703176 getRowCbs ( ) . forEach ( cb => {
31713177 cb . checked = checked ;
@@ -3174,28 +3180,69 @@ function wireSelectAll(fileListContent) {
31743180 updateFileActionButtons ( ) ;
31753181 // No indeterminate state when explicitly toggled
31763182 selectAll . indeterminate = false ;
3183+ selectAll . __lastSelectAllChecked = selectAll . checked ;
3184+ } ;
3185+
3186+ const handleSelectAllChange = ( ) => {
3187+ applySelectAll ( ) ;
3188+ } ;
3189+
3190+ // Toggle all rows when the header checkbox changes
3191+ selectAll . addEventListener ( 'change' , handleSelectAllChange ) ;
3192+
3193+ // Some UI layers can swallow left-click toggles. Track state and force toggle if needed.
3194+ selectAll . addEventListener ( 'mousedown' , ( e ) => {
3195+ if ( e . button !== 0 ) return ;
3196+ selectAll . __lastSelectAllChecked = selectAll . checked ;
3197+ } ) ;
3198+
3199+ selectAll . addEventListener ( 'click' , ( e ) => {
3200+ if ( e . button !== 0 ) return ;
3201+ if ( selectAll . disabled ) return ;
3202+ const last = selectAll . __lastSelectAllChecked ;
3203+ if ( last === selectAll . checked ) {
3204+ selectAll . checked = ! selectAll . checked ;
3205+ handleSelectAllChange ( ) ;
3206+ }
3207+ selectAll . __lastSelectAllChecked = selectAll . checked ;
31773208 } ) ;
31783209
3210+ const headerCell = selectAll . closest ( 'th' ) ;
3211+ if ( headerCell && ! headerCell . __wiredSelectAllCell ) {
3212+ headerCell . __wiredSelectAllCell = true ;
3213+ headerCell . addEventListener ( 'click' , ( e ) => {
3214+ if ( e . target === selectAll ) return ;
3215+ if ( selectAll . disabled ) return ;
3216+ selectAll . checked = ! selectAll . checked ;
3217+ handleSelectAllChange ( ) ;
3218+ } ) ;
3219+ }
3220+
31793221 // Keep header checkbox state in sync with row selections
31803222 const syncHeader = ( ) => {
3223+ const master = getSelectAll ( ) ;
3224+ if ( ! master ) return ;
31813225 const cbs = getRowCbs ( ) ;
31823226 const total = cbs . length ;
31833227 const checked = cbs . filter ( cb => cb . checked ) . length ;
31843228 if ( ! total ) {
3185- selectAll . checked = false ;
3186- selectAll . indeterminate = false ;
3229+ master . checked = false ;
3230+ master . indeterminate = false ;
31873231 return ;
31883232 }
3189- selectAll . checked = checked === total ;
3190- selectAll . indeterminate = checked > 0 && checked < total ;
3233+ master . checked = checked === total ;
3234+ master . indeterminate = checked > 0 && checked < total ;
31913235 } ;
31923236
31933237 // Listen for any row checkbox changes to refresh header state
3194- fileListContent . addEventListener ( 'change' , ( e ) => {
3195- if ( e . target && e . target . classList . contains ( 'file-checkbox' ) ) {
3196- syncHeader ( ) ;
3197- }
3198- } ) ;
3238+ if ( ! fileListContent . __wiredSelectAllContainer ) {
3239+ fileListContent . __wiredSelectAllContainer = true ;
3240+ fileListContent . addEventListener ( 'change' , ( e ) => {
3241+ if ( e . target && e . target . classList . contains ( 'file-checkbox' ) ) {
3242+ syncHeader ( ) ;
3243+ }
3244+ } ) ;
3245+ }
31993246
32003247 // Initial sync on mount
32013248 syncHeader ( ) ;
@@ -6001,7 +6048,7 @@ export async function loadFileList(folderParam, options = {}) {
60016048
60026049 // Re-render table view once folders are known so they appear inline above files
60036050 if ( window . viewMode === "table" && reqId === __fileListReqSeq [ pane ] ) {
6004- renderFileTable ( folder ) ;
6051+ renderFileTable ( folder , fileListContainer , undefined , { preserveSelection : true } ) ;
60056052 }
60066053 }
60076054 } catch ( e ) {
@@ -6755,9 +6802,30 @@ async function openDefaultFileFromHover(file) {
67556802 */
67566803
67576804
6758- export async function renderFileTable ( folder , container , subfolders ) {
6805+ function getSelectedFileValuesForList ( listEl ) {
6806+ if ( ! listEl ) return [ ] ;
6807+ return Array . from ( listEl . querySelectorAll ( 'tbody .file-checkbox:checked' ) )
6808+ . map ( cb => cb . value ) ;
6809+ }
6810+
6811+ function restoreSelectedFileValues ( listEl , selectedValues ) {
6812+ if ( ! listEl || ! Array . isArray ( selectedValues ) || ! selectedValues . length ) return ;
6813+ const selectedSet = new Set ( selectedValues . map ( v => String ( v ) ) ) ;
6814+ listEl . querySelectorAll ( 'tbody .file-checkbox' ) . forEach ( cb => {
6815+ const raw = cb . value ;
6816+ const decoded = decodeHtmlEntities ( raw ) ;
6817+ if ( selectedSet . has ( raw ) || ( decoded && selectedSet . has ( decoded ) ) ) {
6818+ cb . checked = true ;
6819+ updateRowHighlight ( cb ) ;
6820+ }
6821+ } ) ;
6822+ }
6823+
6824+ export async function renderFileTable ( folder , container , subfolders , options = { } ) {
67596825 clearInlineRenameState ( { restore : false } ) ;
67606826 const fileListContent = container || document . getElementById ( "fileList" ) ;
6827+ const preserveSelection = options && options . preserveSelection === true ;
6828+ let preservedSelection = [ ] ;
67616829 const searchTerm = ( window . currentSearchTerm || "" ) . toLowerCase ( ) ;
67626830 const itemsPerPageSetting = parseInt ( localStorage . getItem ( "itemsPerPage" ) || "50" , 10 ) ;
67636831 let currentPage = window . currentPage || 1 ;
@@ -6785,6 +6853,11 @@ export async function renderFileTable(folder, container, subfolders) {
67856853// NEW: sort folders according to current sort order (name / size)
67866854const subfoldersSorted = await sortSubfoldersForCurrentOrder ( allSubfolders ) ;
67876855
6856+ if ( preserveSelection ) {
6857+ // Capture after async work so user selections during the wait aren't lost.
6858+ preservedSelection = getSelectedFileValuesForList ( fileListContent ) ;
6859+ }
6860+
67886861 const totalFiles = filteredFiles . length ;
67896862 const totalFolders = subfoldersSorted . length ;
67906863 const totalRows = totalFiles + totalFolders ;
@@ -6894,6 +6967,7 @@ const subfoldersSorted = await sortSubfoldersForCurrentOrder(allSubfolders);
68946967
68956968 fileListContent . innerHTML = combinedTopHTML + headerHTML + rowsHTML + bottomControlsHTML ;
68966969 updatePaneWidthClasses ( ) ;
6970+ restoreSelectedFileValues ( fileListContent , preservedSelection ) ;
68976971
68986972 ( function rightAlignSizeColumn ( ) {
68996973 const table = fileListContent . querySelector ( "table.filr-table" ) ;
0 commit comments