44import  {  Logger ,  LogLevel  }  from  '../Platform/Logging/Logger' ; 
55import  {  ConsoleLogger  }  from  '../Platform/Logging/Loggers' ; 
66
7+ // Minimal File System Access API typings 
8+ interface  FileSystemWritableFileStream  { 
9+   write ( data : BufferSource  |  Blob  |  Uint8Array ) : Promise < void > ; 
10+   close ( ) : Promise < void > ; 
11+   abort ( ) : Promise < void > ; 
12+ } 
13+ interface  FileSystemFileHandle  { 
14+   createWritable ( ) : Promise < FileSystemWritableFileStream > ; 
15+ } 
16+ interface  SaveFilePickerOptions  { 
17+   suggestedName ?: string ; 
18+ } 
19+ declare  global { 
20+   interface  Window  { 
21+     showSaveFilePicker ?: ( options ?: SaveFilePickerOptions )  =>  Promise < FileSystemFileHandle > ; 
22+   } 
23+ } 
24+ 
725export  interface  MediaLoadResult  { 
826  success : boolean ; 
927  fromCache : boolean ; 
@@ -27,18 +45,18 @@ export class BinaryMedia {
2745
2846  private  static  tracked : WeakMap < HTMLElement ,  {  url : string ;  cacheKey : string ;  attr : 'src'  |  'href'  } >  =  new  WeakMap ( ) ; 
2947
30-   private  static  observer :  MutationObserver   |   null   =   null ; 
48+   private  static  observersByParent :  WeakMap < Element ,   MutationObserver >   =   new   WeakMap ( ) ; 
3149
3250  private  static  controllers : WeakMap < HTMLElement ,  AbortController >  =  new  WeakMap ( ) ; 
3351
34-   private  static  initializeObserver ( ) : void   { 
35-     if  ( this . observer )  { 
52+   private  static  initializeParentObserver ( parent :  Element ) : void   { 
53+     if  ( this . observersByParent . has ( parent ) )  { 
3654      return ; 
3755    } 
3856
39-     this . observer  =  new  MutationObserver ( ( mutations )  =>  { 
57+     const   observer  =  new  MutationObserver ( ( mutations )  =>  { 
4058      for  ( const  mutation  of  mutations )  { 
41-         // Handle removed nodes 
59+         // Handle removed nodes within this parent subtree  
4260        if  ( mutation . type  ===  'childList' )  { 
4361          for  ( const  node  of  Array . from ( mutation . removedNodes ) )  { 
4462            if  ( node . nodeType  ===  Node . ELEMENT_NODE )  { 
@@ -60,14 +78,13 @@ export class BinaryMedia {
6078          } 
6179        } 
6280
63-         // Handle attribute  changes on tracked elements  
81+         // Attribute  changes in this subtree  
6482        if  ( mutation . type  ===  'attributes' )  { 
6583          const  attrName  =  ( mutation  as  MutationRecord ) . attributeName ; 
6684          if  ( attrName  ===  'src'  ||  attrName  ===  'href' )  { 
6785            const  element  =  mutation . target  as  HTMLElement ; 
6886            const  tracked  =  this . tracked . get ( element ) ; 
6987            if  ( tracked  &&  tracked . attr  ===  attrName )  { 
70- 
7188              const  current  =  element . getAttribute ( attrName )  ||  '' ; 
7289              if  ( ! current  ||  current  !==  tracked . url )  { 
7390                this . revokeTrackedUrl ( element ) ; 
@@ -78,11 +95,13 @@ export class BinaryMedia {
7895      } 
7996    } ) ; 
8097
81-     this . observer . observe ( document . body ,  { 
98+     observer . observe ( parent ,  { 
8299      childList : true , 
83100      attributes : true , 
84101      attributeFilter : [ 'src' ,  'href' ] , 
85102    } ) ; 
103+ 
104+     this . observersByParent . set ( parent ,  observer ) ; 
86105  } 
87106
88107  private  static  revokeTrackedUrl ( el : HTMLElement ) : void   { 
@@ -124,7 +143,11 @@ export class BinaryMedia {
124143      return  {  success : false ,  fromCache : false ,  objectUrl : null ,  error : 'Invalid parameters'  } ; 
125144    } 
126145
127-     this . initializeObserver ( ) ; 
146+     // Ensure we are observing this element's parent 
147+     const  parent  =  element . parentElement ; 
148+     if  ( parent )  { 
149+       this . initializeParentObserver ( parent ) ; 
150+     } 
128151
129152    // If there was a previous different key for this element, abort its in-flight operation 
130153    const  previousKey  =  this . activeCacheKey . get ( element ) ; 
@@ -327,9 +350,9 @@ export class BinaryMedia {
327350      const  readable  =  await  streamRef . stream ( ) ; 
328351
329352      // Native picker direct-to-file streaming available 
330-       if  ( typeof  ( window   as   any ) . showSaveFilePicker  ===  'function' )  {   // eslint-disable-line @typescript-eslint/no-explicit-any 
353+       if  ( typeof  window . showSaveFilePicker  ===  'function' )  { 
331354        try  { 
332-           const  handle  =  await  ( window   as   any ) . showSaveFilePicker ( {  suggestedName : fileName } ) ;   // eslint-disable-line @typescript-eslint/no-explicit-any 
355+           const  handle  =  await  window . showSaveFilePicker ( {  suggestedName : fileName   } ) ; 
333356
334357          const  writer  =  await  handle . createWritable ( ) ; 
335358          const  writeResult  =  await  this . writeStreamToFile ( element ,  readable ,  writer ,  totalBytes ,  controller ) ; 
@@ -465,7 +488,7 @@ export class BinaryMedia {
465488  private  static  async  writeStreamToFile ( 
466489    element : HTMLElement , 
467490    stream : ReadableStream < Uint8Array > , 
468-     writer : any ,   // eslint-disable-line @typescript-eslint/no-explicit-any 
491+     writer : FileSystemWritableFileStream , 
469492    totalBytes : number  |  null , 
470493    controller ?: AbortController 
471494  ) : Promise < 'success'  |  'aborted'  |  'error' >  { 
0 commit comments