char-slop/berry
multipurpose self-modifiable agentic harness
git clone https://git.t4t.associates/char-slop/berry
ece94e9
main
1type Chunk = { 2element :HTMLElement ; 3near :boolean ; 4rich :boolean ; 5height :number ; 6dirty :boolean ; 7render ( rich :boolean ) :void ; 8visible ( visible :boolean ) :void ; 9}; 10 11/** Rich markup near the viewport, searchable text elsewhere, without disturbing a selection or find result. */ 12export function viewport ( root :HTMLElement ) { 13const chunks = new Map < Element , Chunk >(); 14const pending = new Set < Chunk >(); 15const pinned = new Set < Chunk >(); 16let frame = 0 ; 17let width = 0 ; 18let disposed = false ; 19 20function schedule () { 21if ( frame || disposed ) return ; 22frame = requestAnimationFrame ( flush ); 23} 24 25function flush () { 26frame = 0 ; 27const until = performance . now () + 4 ; 28const selection = document . getSelection (); 29for ( const chunk of pending ) { 30pending . delete ( chunk ); 31if ( chunk . near === chunk . rich && ! chunk . dirty ) continue ; 32// firefox exposes the active native find match as a selection, too 33if ( 34chunk . element . contains ( document . activeElement ) || 35( selection && 36Array . from ({ length :selection . rangeCount }, ( _ , i ) => selection . getRangeAt ( i )) 37. some (( range ) => range . intersectsNode ( chunk . element ))) 38) { 39pinned . add ( chunk ); 40continue ; 41} 42if ( ! chunk . near && chunk . height > 0 ) { 43chunk . element . style . height = ` ${ chunk . height } px` ; 44} else chunk . element . style . removeProperty ( "height" ); 45chunk . rich = chunk . near ; 46chunk . dirty = false ; 47chunk . render ( chunk . rich ); 48if ( chunk . rich ) sizes ?. observe ( chunk . element ); 49else sizes ?. unobserve ( chunk . element ); 50if ( performance . now () >= until ) break ; 51} 52if ( pending . size ) schedule (); 53} 54 55const nearby = typeof IntersectionObserver === "undefined" ?null :new IntersectionObserver ( 56( entries ) => { 57for ( const entry of entries ) { 58const chunk = chunks . get ( entry . target ); 59if ( ! chunk ) continue ; 60chunk . near = entry . isIntersecting ; 61pending . add ( chunk ); 62} 63schedule (); 64}, 65{ root, rootMargin :"800px 0px" }, 66); 67const visible = typeof IntersectionObserver === "undefined" ?null :new IntersectionObserver ( 68( entries ) => { 69for ( const entry of entries ) chunks . get ( entry . target )?. visible ( entry . isIntersecting ); 70}, 71{ root}, 72); 73const sizes = typeof ResizeObserver === "undefined" ?null :new ResizeObserver (( entries ) => { 74for ( const entry of entries ) { 75if ( entry . target === root ) { 76if ( width === entry . contentRect . width ) continue ; 77width = entry . contentRect . width ; 78for ( const chunk of chunks . values ()) { 79chunk . height = 0 ; 80chunk . element . style . removeProperty ( "height" ); 81} 82} else { 83const chunk = chunks . get ( entry . target ); 84if ( chunk ?. rich ) chunk . height = entry . contentRect . height ; 85} 86} 87}); 88sizes ?. observe ( root ); 89 90const release = () => { 91for ( const chunk of pinned ) pending . add ( chunk ); 92pinned . clear (); 93if ( pending . size ) schedule (); 94}; 95document . addEventListener ( "selectionchange" , release ); 96root . addEventListener ( "focusout" , release ); 97 98return { 99add ( element :HTMLElement , render :Chunk [ "render" ], onVisible :Chunk [ "visible" ]) { 100const chunk :Chunk = { 101 element, 102near :nearby === null , 103rich :nearby === null , 104height :0 , 105dirty :false , 106 render, 107visible :onVisible , 108}; 109chunks . set ( element , chunk ); 110render ( chunk . rich ); 111nearby ?. observe ( element ); 112visible ?. observe ( element ); 113return { 114redraw () { 115if ( ! chunk . rich ) return ; 116chunk . dirty = true ; 117pending . add ( chunk ); 118schedule (); 119}, 120dispose () { 121nearby ?. unobserve ( element ); 122visible ?. unobserve ( element ); 123sizes ?. unobserve ( element ); 124chunks . delete ( element ); 125pending . delete ( chunk ); 126pinned . delete ( chunk ); 127}, 128}; 129}, 130dispose () { 131disposed = true ; 132cancelAnimationFrame ( frame ); 133nearby ?. disconnect (); 134visible ?. disconnect (); 135sizes ?. disconnect (); 136chunks . clear (); 137pending . clear (); 138pinned . clear (); 139document . removeEventListener ( "selectionchange" , release ); 140root . removeEventListener ( "focusout" , release ); 141}, 142}; 143}