@@ -297,33 +297,33 @@ class ModelManager {
297297 }
298298
299299 /**
300- * Find GGUF files on disk that aren't tracked in the model list.
301- * Returns array of orphaned file info.
300+ * Find files/directories on disk that aren't tracked in model registries.
301+ * Scans both the text models directory (for untracked files) and
302+ * the image models directory (for untracked directories).
302303 */
303304 async getOrphanedFiles ( ) : Promise < Array < { name : string ; path : string ; size : number } > > {
304305 await this . initialize ( ) ;
305306 const orphaned : Array < { name : string ; path : string ; size : number } > = [ ] ;
306307
307308 try {
308- const dirExists = await RNFS . exists ( this . modelsDir ) ;
309- if ( ! dirExists ) return orphaned ;
310-
311- const files = await RNFS . readDir ( this . modelsDir ) ;
312- const models = await this . getDownloadedModels ( ) ;
309+ // Scan text models directory for untracked files
310+ const modelsDirExists = await RNFS . exists ( this . modelsDir ) ;
311+ if ( modelsDirExists ) {
312+ const files = await RNFS . readDir ( this . modelsDir ) ;
313+ const models = await this . getDownloadedModels ( ) ;
313314
314- // Get all tracked file paths (including mmproj)
315- const trackedPaths = new Set < string > ( ) ;
316- for ( const model of models ) {
317- trackedPaths . add ( model . filePath ) ;
318- if ( model . mmProjPath ) {
319- trackedPaths . add ( model . mmProjPath ) ;
315+ // Get all tracked file paths (including mmproj)
316+ const trackedPaths = new Set < string > ( ) ;
317+ for ( const model of models ) {
318+ trackedPaths . add ( model . filePath ) ;
319+ if ( model . mmProjPath ) {
320+ trackedPaths . add ( model . mmProjPath ) ;
321+ }
320322 }
321- }
322323
323- // Find GGUF files not in tracked list
324- for ( const file of files ) {
325- if ( file . isFile ( ) && file . name . endsWith ( '.gguf' ) ) {
326- if ( ! trackedPaths . has ( file . path ) ) {
324+ // Find any files not in tracked list (not just .gguf)
325+ for ( const file of files ) {
326+ if ( file . isFile ( ) && ! trackedPaths . has ( file . path ) ) {
327327 orphaned . push ( {
328328 name : file . name ,
329329 path : file . path ,
@@ -332,6 +332,39 @@ class ModelManager {
332332 }
333333 }
334334 }
335+
336+ // Scan image models directory for untracked directories
337+ const imageDirExists = await RNFS . exists ( this . imageModelsDir ) ;
338+ if ( imageDirExists ) {
339+ const items = await RNFS . readDir ( this . imageModelsDir ) ;
340+ const imageModels = await this . getDownloadedImageModels ( ) ;
341+ const trackedImagePaths = new Set ( imageModels . map ( m => m . modelPath ) ) ;
342+
343+ for ( const item of items ) {
344+ if ( ! trackedImagePaths . has ( item . path ) ) {
345+ let totalSize = 0 ;
346+ if ( item . isDirectory ( ) ) {
347+ try {
348+ const dirFiles = await RNFS . readDir ( item . path ) ;
349+ for ( const f of dirFiles ) {
350+ if ( f . isFile ( ) ) {
351+ totalSize += typeof f . size === 'string' ? parseInt ( f . size , 10 ) : f . size ;
352+ }
353+ }
354+ } catch {
355+ // Can't read directory, use 0
356+ }
357+ } else {
358+ totalSize = typeof item . size === 'string' ? parseInt ( item . size , 10 ) : item . size ;
359+ }
360+ orphaned . push ( {
361+ name : item . name ,
362+ path : item . path ,
363+ size : totalSize ,
364+ } ) ;
365+ }
366+ }
367+ }
335368 } catch ( error ) {
336369 console . error ( '[ModelManager] Error scanning for orphaned files:' , error ) ;
337370 }
@@ -340,12 +373,15 @@ class ModelManager {
340373 }
341374
342375 /**
343- * Delete an orphaned file from disk.
376+ * Delete an orphaned file or directory from disk.
344377 */
345378 async deleteOrphanedFile ( filePath : string ) : Promise < void > {
346379 try {
347- await RNFS . unlink ( filePath ) ;
348- console . log ( '[ModelManager] Deleted orphaned file:' , filePath ) ;
380+ const exists = await RNFS . exists ( filePath ) ;
381+ if ( exists ) {
382+ await RNFS . unlink ( filePath ) ;
383+ console . log ( '[ModelManager] Deleted orphaned file/directory:' , filePath ) ;
384+ }
349385 } catch ( error ) {
350386 console . error ( '[ModelManager] Failed to delete orphaned file:' , error ) ;
351387 throw error ;
@@ -1080,6 +1116,12 @@ class ModelManager {
10801116
10811117 const fileSize = typeof item . size === 'string' ? parseInt ( item . size , 10 ) : item . size ;
10821118
1119+ // Skip tiny files (< 1MB) — likely partial/failed downloads, not valid models
1120+ if ( fileSize < 1_000_000 ) {
1121+ console . log ( `[ModelManager] Skipping tiny file as likely partial download: ${ item . name } (${ fileSize } bytes)` ) ;
1122+ continue ;
1123+ }
1124+
10831125 // Try to parse quantization from filename
10841126 const quantMatch = item . name . match ( / [ _ - ] ( Q \d + [ _ \w ] * | f 1 6 | f 3 2 ) / i) ;
10851127 const quantization = quantMatch ? quantMatch [ 1 ] . toUpperCase ( ) : 'Unknown' ;
0 commit comments