2026-02-22 11:38:03 +01:00
<!doctype html>
< html lang = "en" >
< head >
< title > Code coverage report for src/App.tsx< / title >
< meta charset = "utf-8" / >
< link rel = "stylesheet" href = "../prettify.css" / >
< link rel = "stylesheet" href = "../base.css" / >
< link rel = "shortcut icon" type = "image/x-icon" href = "../favicon.png" / >
< meta name = "viewport" content = "width=device-width, initial-scale=1" / >
< style type = 'text/css' >
.coverage-summary .sorter {
background-image: url(../sort-arrow-sprite.png);
}
< / style >
< / head >
< body >
< div class = 'wrapper' >
< div class = 'pad1' >
< h1 > < a href = "../index.html" > All files< / a > / < a href = "index.html" > src< / a > App.tsx< / h1 >
< div class = 'clearfix' >
< div class = 'fl pad1y space-right2' >
2026-02-25 21:35:30 +01:00
< span class = "strong" > 47.5% < / span >
2026-02-22 11:38:03 +01:00
< span class = "quiet" > Statements< / span >
2026-02-25 21:35:30 +01:00
< span class = 'fraction' > 57/120< / span >
2026-02-22 11:38:03 +01:00
< / div >
< div class = 'fl pad1y space-right2' >
2026-02-25 21:35:30 +01:00
< span class = "strong" > 40.47% < / span >
2026-02-22 11:38:03 +01:00
< span class = "quiet" > Branches< / span >
2026-02-25 21:35:30 +01:00
< span class = 'fraction' > 17/42< / span >
2026-02-22 11:38:03 +01:00
< / div >
< div class = 'fl pad1y space-right2' >
2026-02-25 21:35:30 +01:00
< span class = "strong" > 20.58% < / span >
2026-02-22 11:38:03 +01:00
< span class = "quiet" > Functions< / span >
2026-02-25 21:35:30 +01:00
< span class = 'fraction' > 7/34< / span >
2026-02-22 11:38:03 +01:00
< / div >
< div class = 'fl pad1y space-right2' >
2026-02-25 21:35:30 +01:00
< span class = "strong" > 57.73% < / span >
2026-02-22 11:38:03 +01:00
< span class = "quiet" > Lines< / span >
2026-02-25 21:35:30 +01:00
< span class = 'fraction' > 56/97< / span >
2026-02-22 11:38:03 +01:00
< / div >
< / div >
< p class = "quiet" >
Press < em > n< / em > or < em > j< / em > to go to the next uncovered block, < em > b< / em > , < em > p< / em > or < em > k< / em > for the previous block.
< / p >
< template id = "filterTemplate" >
< div class = "quiet" >
Filter:
< input type = "search" id = "fileSearch" >
< / div >
< / template >
< / div >
< div class = 'status-line low' > < / div >
< pre > < table class = "coverage" >
< tr > < td class = "line-count quiet" > < a name = 'L1' > < / a > < a href = '#L1' > 1< / a >
< a name = 'L2' > < / a > < a href = '#L2' > 2< / a >
< a name = 'L3' > < / a > < a href = '#L3' > 3< / a >
< a name = 'L4' > < / a > < a href = '#L4' > 4< / a >
< a name = 'L5' > < / a > < a href = '#L5' > 5< / a >
< a name = 'L6' > < / a > < a href = '#L6' > 6< / a >
< a name = 'L7' > < / a > < a href = '#L7' > 7< / a >
< a name = 'L8' > < / a > < a href = '#L8' > 8< / a >
< a name = 'L9' > < / a > < a href = '#L9' > 9< / a >
< a name = 'L10' > < / a > < a href = '#L10' > 10< / a >
< a name = 'L11' > < / a > < a href = '#L11' > 11< / a >
< a name = 'L12' > < / a > < a href = '#L12' > 12< / a >
< a name = 'L13' > < / a > < a href = '#L13' > 13< / a >
< a name = 'L14' > < / a > < a href = '#L14' > 14< / a >
< a name = 'L15' > < / a > < a href = '#L15' > 15< / a >
< a name = 'L16' > < / a > < a href = '#L16' > 16< / a >
< a name = 'L17' > < / a > < a href = '#L17' > 17< / a >
< a name = 'L18' > < / a > < a href = '#L18' > 18< / a >
< a name = 'L19' > < / a > < a href = '#L19' > 19< / a >
< a name = 'L20' > < / a > < a href = '#L20' > 20< / a >
< a name = 'L21' > < / a > < a href = '#L21' > 21< / a >
< a name = 'L22' > < / a > < a href = '#L22' > 22< / a >
< a name = 'L23' > < / a > < a href = '#L23' > 23< / a >
< a name = 'L24' > < / a > < a href = '#L24' > 24< / a >
< a name = 'L25' > < / a > < a href = '#L25' > 25< / a >
< a name = 'L26' > < / a > < a href = '#L26' > 26< / a >
< a name = 'L27' > < / a > < a href = '#L27' > 27< / a >
< a name = 'L28' > < / a > < a href = '#L28' > 28< / a >
< a name = 'L29' > < / a > < a href = '#L29' > 29< / a >
< a name = 'L30' > < / a > < a href = '#L30' > 30< / a >
< a name = 'L31' > < / a > < a href = '#L31' > 31< / a >
< a name = 'L32' > < / a > < a href = '#L32' > 32< / a >
< a name = 'L33' > < / a > < a href = '#L33' > 33< / a >
< a name = 'L34' > < / a > < a href = '#L34' > 34< / a >
< a name = 'L35' > < / a > < a href = '#L35' > 35< / a >
< a name = 'L36' > < / a > < a href = '#L36' > 36< / a >
< a name = 'L37' > < / a > < a href = '#L37' > 37< / a >
< a name = 'L38' > < / a > < a href = '#L38' > 38< / a >
< a name = 'L39' > < / a > < a href = '#L39' > 39< / a >
< a name = 'L40' > < / a > < a href = '#L40' > 40< / a >
< a name = 'L41' > < / a > < a href = '#L41' > 41< / a >
< a name = 'L42' > < / a > < a href = '#L42' > 42< / a >
< a name = 'L43' > < / a > < a href = '#L43' > 43< / a >
< a name = 'L44' > < / a > < a href = '#L44' > 44< / a >
< a name = 'L45' > < / a > < a href = '#L45' > 45< / a >
< a name = 'L46' > < / a > < a href = '#L46' > 46< / a >
< a name = 'L47' > < / a > < a href = '#L47' > 47< / a >
< a name = 'L48' > < / a > < a href = '#L48' > 48< / a >
< a name = 'L49' > < / a > < a href = '#L49' > 49< / a >
< a name = 'L50' > < / a > < a href = '#L50' > 50< / a >
< a name = 'L51' > < / a > < a href = '#L51' > 51< / a >
< a name = 'L52' > < / a > < a href = '#L52' > 52< / a >
< a name = 'L53' > < / a > < a href = '#L53' > 53< / a >
< a name = 'L54' > < / a > < a href = '#L54' > 54< / a >
< a name = 'L55' > < / a > < a href = '#L55' > 55< / a >
< a name = 'L56' > < / a > < a href = '#L56' > 56< / a >
< a name = 'L57' > < / a > < a href = '#L57' > 57< / a >
< a name = 'L58' > < / a > < a href = '#L58' > 58< / a >
< a name = 'L59' > < / a > < a href = '#L59' > 59< / a >
< a name = 'L60' > < / a > < a href = '#L60' > 60< / a >
< a name = 'L61' > < / a > < a href = '#L61' > 61< / a >
< a name = 'L62' > < / a > < a href = '#L62' > 62< / a >
< a name = 'L63' > < / a > < a href = '#L63' > 63< / a >
< a name = 'L64' > < / a > < a href = '#L64' > 64< / a >
< a name = 'L65' > < / a > < a href = '#L65' > 65< / a >
< a name = 'L66' > < / a > < a href = '#L66' > 66< / a >
< a name = 'L67' > < / a > < a href = '#L67' > 67< / a >
< a name = 'L68' > < / a > < a href = '#L68' > 68< / a >
< a name = 'L69' > < / a > < a href = '#L69' > 69< / a >
< a name = 'L70' > < / a > < a href = '#L70' > 70< / a >
< a name = 'L71' > < / a > < a href = '#L71' > 71< / a >
< a name = 'L72' > < / a > < a href = '#L72' > 72< / a >
< a name = 'L73' > < / a > < a href = '#L73' > 73< / a >
< a name = 'L74' > < / a > < a href = '#L74' > 74< / a >
< a name = 'L75' > < / a > < a href = '#L75' > 75< / a >
< a name = 'L76' > < / a > < a href = '#L76' > 76< / a >
< a name = 'L77' > < / a > < a href = '#L77' > 77< / a >
< a name = 'L78' > < / a > < a href = '#L78' > 78< / a >
< a name = 'L79' > < / a > < a href = '#L79' > 79< / a >
< a name = 'L80' > < / a > < a href = '#L80' > 80< / a >
< a name = 'L81' > < / a > < a href = '#L81' > 81< / a >
< a name = 'L82' > < / a > < a href = '#L82' > 82< / a >
< a name = 'L83' > < / a > < a href = '#L83' > 83< / a >
< a name = 'L84' > < / a > < a href = '#L84' > 84< / a >
< a name = 'L85' > < / a > < a href = '#L85' > 85< / a >
< a name = 'L86' > < / a > < a href = '#L86' > 86< / a >
< a name = 'L87' > < / a > < a href = '#L87' > 87< / a >
< a name = 'L88' > < / a > < a href = '#L88' > 88< / a >
< a name = 'L89' > < / a > < a href = '#L89' > 89< / a >
< a name = 'L90' > < / a > < a href = '#L90' > 90< / a >
< a name = 'L91' > < / a > < a href = '#L91' > 91< / a >
< a name = 'L92' > < / a > < a href = '#L92' > 92< / a >
< a name = 'L93' > < / a > < a href = '#L93' > 93< / a >
< a name = 'L94' > < / a > < a href = '#L94' > 94< / a >
< a name = 'L95' > < / a > < a href = '#L95' > 95< / a >
< a name = 'L96' > < / a > < a href = '#L96' > 96< / a >
< a name = 'L97' > < / a > < a href = '#L97' > 97< / a >
< a name = 'L98' > < / a > < a href = '#L98' > 98< / a >
< a name = 'L99' > < / a > < a href = '#L99' > 99< / a >
< a name = 'L100' > < / a > < a href = '#L100' > 100< / a >
< a name = 'L101' > < / a > < a href = '#L101' > 101< / a >
< a name = 'L102' > < / a > < a href = '#L102' > 102< / a >
< a name = 'L103' > < / a > < a href = '#L103' > 103< / a >
< a name = 'L104' > < / a > < a href = '#L104' > 104< / a >
< a name = 'L105' > < / a > < a href = '#L105' > 105< / a >
< a name = 'L106' > < / a > < a href = '#L106' > 106< / a >
< a name = 'L107' > < / a > < a href = '#L107' > 107< / a >
< a name = 'L108' > < / a > < a href = '#L108' > 108< / a >
< a name = 'L109' > < / a > < a href = '#L109' > 109< / a >
< a name = 'L110' > < / a > < a href = '#L110' > 110< / a >
< a name = 'L111' > < / a > < a href = '#L111' > 111< / a >
< a name = 'L112' > < / a > < a href = '#L112' > 112< / a >
< a name = 'L113' > < / a > < a href = '#L113' > 113< / a >
< a name = 'L114' > < / a > < a href = '#L114' > 114< / a >
< a name = 'L115' > < / a > < a href = '#L115' > 115< / a >
< a name = 'L116' > < / a > < a href = '#L116' > 116< / a >
< a name = 'L117' > < / a > < a href = '#L117' > 117< / a >
< a name = 'L118' > < / a > < a href = '#L118' > 118< / a >
< a name = 'L119' > < / a > < a href = '#L119' > 119< / a >
< a name = 'L120' > < / a > < a href = '#L120' > 120< / a >
< a name = 'L121' > < / a > < a href = '#L121' > 121< / a >
< a name = 'L122' > < / a > < a href = '#L122' > 122< / a >
< a name = 'L123' > < / a > < a href = '#L123' > 123< / a >
< a name = 'L124' > < / a > < a href = '#L124' > 124< / a >
< a name = 'L125' > < / a > < a href = '#L125' > 125< / a >
< a name = 'L126' > < / a > < a href = '#L126' > 126< / a >
< a name = 'L127' > < / a > < a href = '#L127' > 127< / a >
< a name = 'L128' > < / a > < a href = '#L128' > 128< / a >
< a name = 'L129' > < / a > < a href = '#L129' > 129< / a >
< a name = 'L130' > < / a > < a href = '#L130' > 130< / a >
< a name = 'L131' > < / a > < a href = '#L131' > 131< / a >
< a name = 'L132' > < / a > < a href = '#L132' > 132< / a >
< a name = 'L133' > < / a > < a href = '#L133' > 133< / a >
< a name = 'L134' > < / a > < a href = '#L134' > 134< / a >
< a name = 'L135' > < / a > < a href = '#L135' > 135< / a >
< a name = 'L136' > < / a > < a href = '#L136' > 136< / a >
< a name = 'L137' > < / a > < a href = '#L137' > 137< / a >
< a name = 'L138' > < / a > < a href = '#L138' > 138< / a >
< a name = 'L139' > < / a > < a href = '#L139' > 139< / a >
< a name = 'L140' > < / a > < a href = '#L140' > 140< / a >
< a name = 'L141' > < / a > < a href = '#L141' > 141< / a >
< a name = 'L142' > < / a > < a href = '#L142' > 142< / a >
< a name = 'L143' > < / a > < a href = '#L143' > 143< / a >
< a name = 'L144' > < / a > < a href = '#L144' > 144< / a >
< a name = 'L145' > < / a > < a href = '#L145' > 145< / a >
< a name = 'L146' > < / a > < a href = '#L146' > 146< / a >
< a name = 'L147' > < / a > < a href = '#L147' > 147< / a >
< a name = 'L148' > < / a > < a href = '#L148' > 148< / a >
< a name = 'L149' > < / a > < a href = '#L149' > 149< / a >
< a name = 'L150' > < / a > < a href = '#L150' > 150< / a >
< a name = 'L151' > < / a > < a href = '#L151' > 151< / a >
< a name = 'L152' > < / a > < a href = '#L152' > 152< / a >
< a name = 'L153' > < / a > < a href = '#L153' > 153< / a >
< a name = 'L154' > < / a > < a href = '#L154' > 154< / a >
< a name = 'L155' > < / a > < a href = '#L155' > 155< / a >
< a name = 'L156' > < / a > < a href = '#L156' > 156< / a >
< a name = 'L157' > < / a > < a href = '#L157' > 157< / a >
< a name = 'L158' > < / a > < a href = '#L158' > 158< / a >
< a name = 'L159' > < / a > < a href = '#L159' > 159< / a >
< a name = 'L160' > < / a > < a href = '#L160' > 160< / a >
< a name = 'L161' > < / a > < a href = '#L161' > 161< / a >
< a name = 'L162' > < / a > < a href = '#L162' > 162< / a >
< a name = 'L163' > < / a > < a href = '#L163' > 163< / a >
< a name = 'L164' > < / a > < a href = '#L164' > 164< / a >
< a name = 'L165' > < / a > < a href = '#L165' > 165< / a >
< a name = 'L166' > < / a > < a href = '#L166' > 166< / a >
< a name = 'L167' > < / a > < a href = '#L167' > 167< / a >
< a name = 'L168' > < / a > < a href = '#L168' > 168< / a >
< a name = 'L169' > < / a > < a href = '#L169' > 169< / a >
< a name = 'L170' > < / a > < a href = '#L170' > 170< / a >
< a name = 'L171' > < / a > < a href = '#L171' > 171< / a >
< a name = 'L172' > < / a > < a href = '#L172' > 172< / a >
< a name = 'L173' > < / a > < a href = '#L173' > 173< / a >
< a name = 'L174' > < / a > < a href = '#L174' > 174< / a >
< a name = 'L175' > < / a > < a href = '#L175' > 175< / a >
< a name = 'L176' > < / a > < a href = '#L176' > 176< / a >
< a name = 'L177' > < / a > < a href = '#L177' > 177< / a >
< a name = 'L178' > < / a > < a href = '#L178' > 178< / a >
< a name = 'L179' > < / a > < a href = '#L179' > 179< / a >
< a name = 'L180' > < / a > < a href = '#L180' > 180< / a >
< a name = 'L181' > < / a > < a href = '#L181' > 181< / a >
< a name = 'L182' > < / a > < a href = '#L182' > 182< / a >
< a name = 'L183' > < / a > < a href = '#L183' > 183< / a >
< a name = 'L184' > < / a > < a href = '#L184' > 184< / a >
< a name = 'L185' > < / a > < a href = '#L185' > 185< / a >
< a name = 'L186' > < / a > < a href = '#L186' > 186< / a >
< a name = 'L187' > < / a > < a href = '#L187' > 187< / a >
< a name = 'L188' > < / a > < a href = '#L188' > 188< / a >
< a name = 'L189' > < / a > < a href = '#L189' > 189< / a >
< a name = 'L190' > < / a > < a href = '#L190' > 190< / a >
< a name = 'L191' > < / a > < a href = '#L191' > 191< / a >
< a name = 'L192' > < / a > < a href = '#L192' > 192< / a >
< a name = 'L193' > < / a > < a href = '#L193' > 193< / a >
< a name = 'L194' > < / a > < a href = '#L194' > 194< / a >
< a name = 'L195' > < / a > < a href = '#L195' > 195< / a >
< a name = 'L196' > < / a > < a href = '#L196' > 196< / a >
< a name = 'L197' > < / a > < a href = '#L197' > 197< / a >
< a name = 'L198' > < / a > < a href = '#L198' > 198< / a >
< a name = 'L199' > < / a > < a href = '#L199' > 199< / a >
< a name = 'L200' > < / a > < a href = '#L200' > 200< / a >
< a name = 'L201' > < / a > < a href = '#L201' > 201< / a >
< a name = 'L202' > < / a > < a href = '#L202' > 202< / a >
< a name = 'L203' > < / a > < a href = '#L203' > 203< / a >
< a name = 'L204' > < / a > < a href = '#L204' > 204< / a >
< a name = 'L205' > < / a > < a href = '#L205' > 205< / a >
< a name = 'L206' > < / a > < a href = '#L206' > 206< / a >
< a name = 'L207' > < / a > < a href = '#L207' > 207< / a >
< a name = 'L208' > < / a > < a href = '#L208' > 208< / a >
< a name = 'L209' > < / a > < a href = '#L209' > 209< / a >
< a name = 'L210' > < / a > < a href = '#L210' > 210< / a >
< a name = 'L211' > < / a > < a href = '#L211' > 211< / a >
< a name = 'L212' > < / a > < a href = '#L212' > 212< / a >
< a name = 'L213' > < / a > < a href = '#L213' > 213< / a >
< a name = 'L214' > < / a > < a href = '#L214' > 214< / a >
< a name = 'L215' > < / a > < a href = '#L215' > 215< / a >
< a name = 'L216' > < / a > < a href = '#L216' > 216< / a >
< a name = 'L217' > < / a > < a href = '#L217' > 217< / a >
< a name = 'L218' > < / a > < a href = '#L218' > 218< / a >
< a name = 'L219' > < / a > < a href = '#L219' > 219< / a >
< a name = 'L220' > < / a > < a href = '#L220' > 220< / a >
< a name = 'L221' > < / a > < a href = '#L221' > 221< / a >
< a name = 'L222' > < / a > < a href = '#L222' > 222< / a >
< a name = 'L223' > < / a > < a href = '#L223' > 223< / a >
< a name = 'L224' > < / a > < a href = '#L224' > 224< / a >
< a name = 'L225' > < / a > < a href = '#L225' > 225< / a >
< a name = 'L226' > < / a > < a href = '#L226' > 226< / a >
< a name = 'L227' > < / a > < a href = '#L227' > 227< / a >
< a name = 'L228' > < / a > < a href = '#L228' > 228< / a >
< a name = 'L229' > < / a > < a href = '#L229' > 229< / a >
< a name = 'L230' > < / a > < a href = '#L230' > 230< / a >
< a name = 'L231' > < / a > < a href = '#L231' > 231< / a >
< a name = 'L232' > < / a > < a href = '#L232' > 232< / a >
< a name = 'L233' > < / a > < a href = '#L233' > 233< / a >
< a name = 'L234' > < / a > < a href = '#L234' > 234< / a >
< a name = 'L235' > < / a > < a href = '#L235' > 235< / a >
< a name = 'L236' > < / a > < a href = '#L236' > 236< / a >
< a name = 'L237' > < / a > < a href = '#L237' > 237< / a >
< a name = 'L238' > < / a > < a href = '#L238' > 238< / a >
< a name = 'L239' > < / a > < a href = '#L239' > 239< / a >
< a name = 'L240' > < / a > < a href = '#L240' > 240< / a >
< a name = 'L241' > < / a > < a href = '#L241' > 241< / a >
< a name = 'L242' > < / a > < a href = '#L242' > 242< / a >
< a name = 'L243' > < / a > < a href = '#L243' > 243< / a >
< a name = 'L244' > < / a > < a href = '#L244' > 244< / a >
< a name = 'L245' > < / a > < a href = '#L245' > 245< / a >
< a name = 'L246' > < / a > < a href = '#L246' > 246< / a >
< a name = 'L247' > < / a > < a href = '#L247' > 247< / a >
< a name = 'L248' > < / a > < a href = '#L248' > 248< / a >
< a name = 'L249' > < / a > < a href = '#L249' > 249< / a >
< a name = 'L250' > < / a > < a href = '#L250' > 250< / a >
< a name = 'L251' > < / a > < a href = '#L251' > 251< / a >
< a name = 'L252' > < / a > < a href = '#L252' > 252< / a >
< a name = 'L253' > < / a > < a href = '#L253' > 253< / a >
< a name = 'L254' > < / a > < a href = '#L254' > 254< / a >
< a name = 'L255' > < / a > < a href = '#L255' > 255< / a >
< a name = 'L256' > < / a > < a href = '#L256' > 256< / a >
< a name = 'L257' > < / a > < a href = '#L257' > 257< / a >
< a name = 'L258' > < / a > < a href = '#L258' > 258< / a >
< a name = 'L259' > < / a > < a href = '#L259' > 259< / a >
< a name = 'L260' > < / a > < a href = '#L260' > 260< / a >
< a name = 'L261' > < / a > < a href = '#L261' > 261< / a >
< a name = 'L262' > < / a > < a href = '#L262' > 262< / a >
< a name = 'L263' > < / a > < a href = '#L263' > 263< / a >
< a name = 'L264' > < / a > < a href = '#L264' > 264< / a >
< a name = 'L265' > < / a > < a href = '#L265' > 265< / a >
< a name = 'L266' > < / a > < a href = '#L266' > 266< / a >
< a name = 'L267' > < / a > < a href = '#L267' > 267< / a >
< a name = 'L268' > < / a > < a href = '#L268' > 268< / a >
< a name = 'L269' > < / a > < a href = '#L269' > 269< / a >
< a name = 'L270' > < / a > < a href = '#L270' > 270< / a >
< a name = 'L271' > < / a > < a href = '#L271' > 271< / a >
< a name = 'L272' > < / a > < a href = '#L272' > 272< / a >
< a name = 'L273' > < / a > < a href = '#L273' > 273< / a >
2026-02-25 21:35:30 +01:00
< a name = 'L274' > < / a > < a href = '#L274' > 274< / a >
< a name = 'L275' > < / a > < a href = '#L275' > 275< / a >
< a name = 'L276' > < / a > < a href = '#L276' > 276< / a >
< a name = 'L277' > < / a > < a href = '#L277' > 277< / a >
< a name = 'L278' > < / a > < a href = '#L278' > 278< / a >
< a name = 'L279' > < / a > < a href = '#L279' > 279< / a >
< a name = 'L280' > < / a > < a href = '#L280' > 280< / a >
< a name = 'L281' > < / a > < a href = '#L281' > 281< / a >
< a name = 'L282' > < / a > < a href = '#L282' > 282< / a >
< a name = 'L283' > < / a > < a href = '#L283' > 283< / a >
< a name = 'L284' > < / a > < a href = '#L284' > 284< / a >
< a name = 'L285' > < / a > < a href = '#L285' > 285< / a >
< a name = 'L286' > < / a > < a href = '#L286' > 286< / a >
< a name = 'L287' > < / a > < a href = '#L287' > 287< / a >
< a name = 'L288' > < / a > < a href = '#L288' > 288< / a >
< a name = 'L289' > < / a > < a href = '#L289' > 289< / a >
< a name = 'L290' > < / a > < a href = '#L290' > 290< / a >
< a name = 'L291' > < / a > < a href = '#L291' > 291< / a >
< a name = 'L292' > < / a > < a href = '#L292' > 292< / a >
< a name = 'L293' > < / a > < a href = '#L293' > 293< / a >
< a name = 'L294' > < / a > < a href = '#L294' > 294< / a >
< a name = 'L295' > < / a > < a href = '#L295' > 295< / a >
< a name = 'L296' > < / a > < a href = '#L296' > 296< / a >
< a name = 'L297' > < / a > < a href = '#L297' > 297< / a >
< a name = 'L298' > < / a > < a href = '#L298' > 298< / a >
< a name = 'L299' > < / a > < a href = '#L299' > 299< / a >
< a name = 'L300' > < / a > < a href = '#L300' > 300< / a >
< a name = 'L301' > < / a > < a href = '#L301' > 301< / a >
< a name = 'L302' > < / a > < a href = '#L302' > 302< / a >
< a name = 'L303' > < / a > < a href = '#L303' > 303< / a > < / td > < td class = "line-coverage quiet" > < span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 1x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 10x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 10x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-neutral" > < / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-no" > < / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-no" > < / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-yes" > 35x< / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-no" > < / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-no" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-neutral" > < / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-neutral" > < / span > < / td > < td class = "text" > < pre class = "prettyprint lang-js" > import { useCallback, useEffect, useRef, useState } from 'react'
2026-02-22 11:38:03 +01:00
import { Sidebar } from './components/Sidebar'
import { NoteList } from './components/NoteList'
import { Editor } from './components/Editor'
import { ResizeHandle } from './components/ResizeHandle'
import { CreateTypeDialog } from './components/CreateTypeDialog'
import { QuickOpenPalette } from './components/QuickOpenPalette'
2026-02-25 21:35:30 +01:00
import { CommandPalette } from './components/CommandPalette'
import { SearchPanel } from './components/SearchPanel'
2026-02-22 11:38:03 +01:00
import { Toast } from './components/Toast'
import { CommitDialog } from './components/CommitDialog'
import { StatusBar } from './components/StatusBar'
2026-02-25 21:35:30 +01:00
import { SettingsPanel } from './components/SettingsPanel'
import { GitHubVaultModal } from './components/GitHubVaultModal'
2026-02-22 11:38:03 +01:00
import { useVaultLoader } from './hooks/useVaultLoader'
2026-02-25 21:35:30 +01:00
import { useSettings } from './hooks/useSettings'
2026-02-22 11:38:03 +01:00
import { useNoteActions } from './hooks/useNoteActions'
2026-02-25 21:35:30 +01:00
import { useEditorSave } from './hooks/useEditorSave'
import { useCommitFlow } from './hooks/useCommitFlow'
import { useViewMode } from './hooks/useViewMode'
import { useEntryActions } from './hooks/useEntryActions'
import { useAppCommands } from './hooks/useAppCommands'
import { useDialogs } from './hooks/useDialogs'
import { useVaultSwitcher } from './hooks/useVaultSwitcher'
import { useGitHistory } from './hooks/useGitHistory'
import { useUpdater } from './hooks/useUpdater'
import { useNavigationHistory } from './hooks/useNavigationHistory'
import { UpdateBanner } from './components/UpdateBanner'
import { setApiKey } from './utils/ai-chat'
import { extractOutgoingLinks } from './utils/wikilinks'
import type { SidebarSelection } from './types'
2026-02-22 11:38:03 +01:00
import './App.css'
// Type declaration for mock content storage
declare global {
interface Window {
__mockContent?: Record< string, string>
}
}
const DEFAULT_SELECTION: SidebarSelection = { kind: 'filter', filter: 'all' }
2026-02-25 21:35:30 +01:00
function useLayoutPanels() {
2026-02-22 11:38:03 +01:00
const [sidebarWidth, setSidebarWidth] = useState(250)
const [noteListWidth, setNoteListWidth] = useState(300)
const [inspectorWidth, setInspectorWidth] = useState(280)
const [inspectorCollapsed, setInspectorCollapsed] = useState(false)
2026-02-25 21:35:30 +01:00
const handleSidebarResize = useCallback(< span class = "fstat-no" title = "function not covered" > (d< / span > elta: number) => < span class = "cstat-no" title = "statement not covered" > setSidebarWidth(< span class = "fstat-no" title = "function not covered" > (w< / span > ) => < span class = "cstat-no" title = "statement not covered" > Math.max(150, Math.min(400, w + delta)))< / span > , [< / span > ])
const handleNoteListResize = useCallback(< span class = "fstat-no" title = "function not covered" > (d< / span > elta: number) => < span class = "cstat-no" title = "statement not covered" > setNoteListWidth(< span class = "fstat-no" title = "function not covered" > (w< / span > ) => < span class = "cstat-no" title = "statement not covered" > Math.max(200, Math.min(500, w + delta)))< / span > , [< / span > ])
const handleInspectorResize = useCallback(< span class = "fstat-no" title = "function not covered" > (d< / span > elta: number) => < span class = "cstat-no" title = "statement not covered" > setInspectorWidth(< span class = "fstat-no" title = "function not covered" > (w< / span > ) => < span class = "cstat-no" title = "statement not covered" > Math.max(200, Math.min(500, w - delta)))< / span > , [< / span > ])
return { sidebarWidth, noteListWidth, inspectorWidth, inspectorCollapsed, setInspectorCollapsed, handleSidebarResize, handleNoteListResize, handleInspectorResize }
}
/** Wraps useEditorSave to also keep outgoingLinks in sync on save and on content change. */
function useEditorSaveWithLinks(config: {
updateContent: (path: string, content: string) => void
updateEntry: (path: string, patch: Partial< import('./types').VaultEntry> ) => void
setTabs: Parameters< typeof useEditorSave> [0]['setTabs']
setToastMessage: (msg: string | null) => void
onAfterSave: () => void
}) {
const { updateContent, updateEntry } = config
const saveContent = useCallback(< span class = "fstat-no" title = "function not covered" > (p< / span > ath: string, content: string) => {
< span class = "cstat-no" title = "statement not covered" > updateContent(path, content)< / span >
< span class = "cstat-no" title = "statement not covered" > updateEntry(path, { outgoingLinks: extractOutgoingLinks(content) })< / span >
}, [updateContent, updateEntry])
const editor = useEditorSave({ updateVaultContent: saveContent, setTabs: config.setTabs, setToastMessage: config.setToastMessage, onAfterSave: config.onAfterSave })
const { handleContentChange: rawOnChange } = editor
const prevLinksKeyRef = useRef('')
const handleContentChange = useCallback(< span class = "fstat-no" title = "function not covered" > (p< / span > ath: string, content: string) => {
< span class = "cstat-no" title = "statement not covered" > rawOnChange(path, content)< / span >
const < span class = "cstat-no" title = "statement not covered" > links = extractOutgoingLinks(content)< / span >
const key = < span class = "cstat-no" title = "statement not covered" > links.join('\0')< / span >
< span class = "cstat-no" title = "statement not covered" > if (key !== prevLinksKeyRef.current) {< / span >
< span class = "cstat-no" title = "statement not covered" > prevLinksKeyRef.current = key< / span >
< span class = "cstat-no" title = "statement not covered" > updateEntry(path, { outgoingLinks: links })< / span >
}
}, [rawOnChange, updateEntry])
return { ...editor, handleContentChange }
}
function App() {
const [selection, setSelection] = useState< SidebarSelection> (DEFAULT_SELECTION)
const layout = useLayoutPanels()
2026-02-22 11:38:03 +01:00
const [toastMessage, setToastMessage] = useState< string | null> (null)
2026-02-25 21:35:30 +01:00
const dialogs = useDialogs()
2026-02-22 11:38:03 +01:00
2026-02-25 21:35:30 +01:00
// onSwitch closure captures `notes` declared below — safe because it's only
// called on user interaction, never during render (refs inside the hook
// guarantee the latest closure is always used).
const vaultSwitcher = useVaultSwitcher({
onSwitch: < span class = "fstat-no" title = "function not covered" > () => {< / span > < span class = "cstat-no" title = "statement not covered" > setSelection(DEFAULT_SELECTION); < span class = "cstat-no" title = "statement not covered" > n< / span > otes.closeAllTabs() }< / span > ,
onToast: < span class = "fstat-no" title = "function not covered" > (m< / span > sg) => < span class = "cstat-no" title = "statement not covered" > setToastMessage(msg),< / span >
})
const vault = useVaultLoader(vaultSwitcher.vaultPath)
const { settings, saveSettings } = useSettings()
useEffect(() => { setApiKey(settings.anthropic_key ?? '') }, [settings.anthropic_key])
2026-02-22 11:38:03 +01:00
2026-02-25 21:35:30 +01:00
const notes = useNoteActions({ addEntry: vault.addEntry, removeEntry: vault.removeEntry, updateContent: vault.updateContent, entries: vault.entries, setToastMessage, updateEntry: vault.updateEntry })
const navHistory = useNavigationHistory()
// Push to navigation history whenever the active tab changes (user-initiated)
const navFromHistoryRef = useRef(false)
2026-02-22 11:38:03 +01:00
useEffect(() => {
2026-02-25 21:35:30 +01:00
< span class = "missing-if-branch" title = "if path not taken" > I< / span > if (notes.activeTabPath & & < span class = "branch-1 cbranch-no" title = "branch not covered" > !navFromHistoryRef.current) {< / span >
< span class = "cstat-no" title = "statement not covered" > navHistory.push(notes.activeTabPath)< / span >
2026-02-22 11:38:03 +01:00
}
2026-02-25 21:35:30 +01:00
navFromHistoryRef.current = false
}, [notes.activeTabPath]) // eslint-disable-line react-hooks/exhaustive-deps -- navHistory.push is stable
2026-02-22 11:38:03 +01:00
2026-02-25 21:35:30 +01:00
const isTabOpen = useCallback(< span class = "fstat-no" title = "function not covered" > (p< / span > ath: string) => < span class = "cstat-no" title = "statement not covered" > notes.tabs.some(< span class = "fstat-no" title = "function not covered" > t => < span class = "cstat-no" title = "statement not covered" > t< / span > .entry.path === path)< / span > , [< / span > notes.tabs])
2026-02-22 11:38:03 +01:00
2026-02-25 21:35:30 +01:00
const handleGoBack = useCallback(< span class = "fstat-no" title = "function not covered" > () => {< / span >
const target = < span class = "cstat-no" title = "statement not covered" > navHistory.goBack(isTabOpen)< / span >
< span class = "cstat-no" title = "statement not covered" > if (target) {< / span >
< span class = "cstat-no" title = "statement not covered" > navFromHistoryRef.current = true< / span >
< span class = "cstat-no" title = "statement not covered" > notes.handleSwitchTab(target)< / span >
}
}, [navHistory, isTabOpen, notes])
const handleGoForward = useCallback(< span class = "fstat-no" title = "function not covered" > () => {< / span >
const target = < span class = "cstat-no" title = "statement not covered" > navHistory.goForward(isTabOpen)< / span >
< span class = "cstat-no" title = "statement not covered" > if (target) {< / span >
< span class = "cstat-no" title = "statement not covered" > navFromHistoryRef.current = true< / span >
< span class = "cstat-no" title = "statement not covered" > notes.handleSwitchTab(target)< / span >
}
}, [navHistory, isTabOpen, notes])
// Mouse button 3/4 (back/forward) and macOS trackpad two-finger swipe
useEffect(() => {
const handleMouseBack = < span class = "fstat-no" title = "function not covered" > (e< / span > : MouseEvent) => {
< span class = "cstat-no" title = "statement not covered" > if (e.button === 3) { < span class = "cstat-no" title = "statement not covered" > e.preventDefault(); < span class = "cstat-no" title = "statement not covered" > h< / span > andleGoBack() }< / span > < / span >
< span class = "cstat-no" title = "statement not covered" > if (e.button === 4) { < span class = "cstat-no" title = "statement not covered" > e.preventDefault(); < span class = "cstat-no" title = "statement not covered" > h< / span > andleGoForward() }< / span > < / span >
}
window.addEventListener('mouseup', handleMouseBack)
// Trackpad swipe: accumulate horizontal wheel delta and trigger on threshold
let accumulatedDeltaX = 0
let resetTimer: ReturnType< typeof setTimeout> | null = null
const SWIPE_THRESHOLD = 120
const handleWheel = < span class = "fstat-no" title = "function not covered" > (e< / span > : WheelEvent) => {
// Only handle horizontal-dominant gestures (trackpad swipe)
< span class = "cstat-no" title = "statement not covered" > if (Math.abs(e.deltaX) < = Math.abs(e.deltaY)) < span class = "cstat-no" title = "statement not covered" > return< / span > < / span >
< span class = "cstat-no" title = "statement not covered" > if (e.ctrlKey || e.metaKey) < span class = "cstat-no" title = "statement not covered" > return // ignore pinch-zoom< / span > < / span >
< span class = "cstat-no" title = "statement not covered" > accumulatedDeltaX += e.deltaX< / span >
< span class = "cstat-no" title = "statement not covered" > if (resetTimer) < span class = "cstat-no" title = "statement not covered" > clearTimeout(resetTimer)< / span > < / span >
< span class = "cstat-no" title = "statement not covered" > resetTimer = setTimeout(< span class = "fstat-no" title = "function not covered" > () => {< / span > < span class = "cstat-no" title = "statement not covered" > accumulatedDeltaX = 0 }< / span > , 300)< / span >
< span class = "cstat-no" title = "statement not covered" > if (accumulatedDeltaX > SWIPE_THRESHOLD) {< / span >
< span class = "cstat-no" title = "statement not covered" > accumulatedDeltaX = 0< / span >
< span class = "cstat-no" title = "statement not covered" > handleGoForward()< / span >
< span class = "cstat-no" title = "statement not covered" > } else if (accumulatedDeltaX < -SWIPE_THRESHOLD) {< / span >
< span class = "cstat-no" title = "statement not covered" > accumulatedDeltaX = 0< / span >
< span class = "cstat-no" title = "statement not covered" > handleGoBack()< / span >
}
}
window.addEventListener('wheel', handleWheel, { passive: true })
return () => {
window.removeEventListener('mouseup', handleMouseBack)
window.removeEventListener('wheel', handleWheel)
< span class = "missing-if-branch" title = "if path not taken" > I< / span > if (resetTimer) < span class = "cstat-no" title = "statement not covered" > clearTimeout(resetTimer)< / span >
}
}, [handleGoBack, handleGoForward])
const { handleSave, handleContentChange, savePendingForPath, savePending } = useEditorSaveWithLinks({
updateContent: vault.updateContent, updateEntry: vault.updateEntry,
setTabs: notes.setTabs, setToastMessage, onAfterSave: vault.loadModifiedFiles,
})
const commitFlow = useCommitFlow({ savePending, loadModifiedFiles: vault.loadModifiedFiles, commitAndPush: vault.commitAndPush, setToastMessage })
const entryActions = useEntryActions({
entries: vault.entries, updateEntry: vault.updateEntry,
handleUpdateFrontmatter: notes.handleUpdateFrontmatter,
handleDeleteProperty: notes.handleDeleteProperty, setToastMessage,
})
const gitHistory = useGitHistory(notes.activeTabPath, vault.loadGitHistory)
2026-02-22 11:38:03 +01:00
const handleCreateType = useCallback(< span class = "fstat-no" title = "function not covered" > (n< / span > ame: string) => {
< span class = "cstat-no" title = "statement not covered" > notes.handleCreateType(name)< / span >
< span class = "cstat-no" title = "statement not covered" > setToastMessage(`Type "${name}" created`)< / span >
2026-02-25 21:35:30 +01:00
}, [notes])
const handleRenameTab = useCallback(< span class = "fstat-no" title = "function not covered" > async (p< / span > ath: string, newTitle: string) => {
< span class = "cstat-no" title = "statement not covered" > await savePendingForPath(path)< / span >
< span class = "cstat-no" title = "statement not covered" > await notes.handleRenameNote(path, newTitle, vaultSwitcher.vaultPath, vault.replaceEntry).then(vault.loadModifiedFiles)< / span >
}, [notes, vaultSwitcher.vaultPath, vault, savePendingForPath])
2026-02-22 11:38:03 +01:00
2026-02-25 21:35:30 +01:00
const { setViewMode, sidebarVisible, noteListVisible } = useViewMode()
const commands = useAppCommands({
activeTabPath: notes.activeTabPath, activeTabPathRef: notes.activeTabPathRef,
handleCloseTabRef: notes.handleCloseTabRef, tabs: notes.tabs,
entries: vault.entries, allContent: vault.allContent,
modifiedCount: vault.modifiedFiles.length, selection,
onQuickOpen: dialogs.openQuickOpen, onCommandPalette: dialogs.openCommandPalette,
onSearch: dialogs.openSearch,
onCreateNote: notes.handleCreateNoteImmediate, onSave: handleSave,
onOpenSettings: dialogs.openSettings,
onTrashNote: entryActions.handleTrashNote, onArchiveNote: entryActions.handleArchiveNote,
onUnarchiveNote: entryActions.handleUnarchiveNote,
onCommitPush: commitFlow.openCommitDialog, onSetViewMode: setViewMode,
onToggleInspector: < span class = "fstat-no" title = "function not covered" > () => < span class = "cstat-no" title = "statement not covered" > l< / span > ayout.setInspectorCollapsed(< span class = "fstat-no" title = "function not covered" > c => < span class = "cstat-no" title = "statement not covered" > !< / span > c)< / span > ,< / span >
onSelect: setSelection, onCloseTab: notes.handleCloseTab,
onSwitchTab: notes.handleSwitchTab, onReplaceActiveTab: notes.handleReplaceActiveTab,
2026-02-22 11:38:03 +01:00
onSelectNote: notes.handleSelectNote,
2026-02-25 21:35:30 +01:00
onGoBack: handleGoBack, onGoForward: handleGoForward,
canGoBack: navHistory.canGoBack, canGoForward: navHistory.canGoForward,
2026-02-22 11:38:03 +01:00
})
2026-02-25 21:35:30 +01:00
const { status: updateStatus, actions: updateActions } = useUpdater()
2026-02-22 11:38:03 +01:00
const activeTab = notes.tabs.find(< span class = "fstat-no" title = "function not covered" > (t< / span > ) => < span class = "cstat-no" title = "statement not covered" > t.entry.path === notes.activeTabPath)< / span > ?? null
return (
< div className="app-shell">
2026-02-25 21:35:30 +01:00
< UpdateBanner status={updateStatus} actions={updateActions} />
2026-02-22 11:38:03 +01:00
< div className="app">
2026-02-25 21:35:30 +01:00
{sidebarVisible & & (
< >
< div className="app__sidebar" style={{ width: layout.sidebarWidth }}>
< Sidebar entries={vault.entries} selection={selection} onSelect={setSelection} onSelectNote={notes.handleSelectNote} onCreateType={notes.handleCreateNoteImmediate} onCreateNewType={dialogs.openCreateType} onCustomizeType={entryActions.handleCustomizeType} onReorderSections={entryActions.handleReorderSections} modifiedCount={vault.modifiedFiles.length} onCommitPush={commitFlow.openCommitDialog} />
< /div>
< ResizeHandle onResize={layout.handleSidebarResize} />
< />
)}
{noteListVisible & & (
< >
< div className="app__note-list" style={{ width: layout.noteListWidth }}>
< NoteList entries={vault.entries} selection={selection} selectedNote={activeTab?.entry ?? null} allContent={vault.allContent} modifiedFiles={vault.modifiedFiles} getNoteStatus={vault.getNoteStatus} onSelectNote={notes.handleSelectNote} onReplaceActiveTab={notes.handleReplaceActiveTab} onCreateNote={notes.handleCreateNoteImmediate} />
< /div>
< ResizeHandle onResize={layout.handleNoteListResize} />
< />
)}
2026-02-22 11:38:03 +01:00
< div className="app__editor">
< Editor
tabs={notes.tabs}
activeTabPath={notes.activeTabPath}
entries={vault.entries}
onSwitchTab={notes.handleSwitchTab}
onCloseTab={notes.handleCloseTab}
onReorderTabs={notes.handleReorderTabs}
onNavigateWikilink={notes.handleNavigateWikilink}
onLoadDiff={vault.loadDiff}
onLoadDiffAtCommit={vault.loadDiffAtCommit}
2026-02-25 21:35:30 +01:00
getNoteStatus={vault.getNoteStatus}
onCreateNote={notes.handleCreateNoteImmediate}
inspectorCollapsed={layout.inspectorCollapsed}
onToggleInspector={< span class = "fstat-no" title = "function not covered" > () => < span class = "cstat-no" title = "statement not covered" > l< / span > ayout.setInspectorCollapsed(< span class = "fstat-no" title = "function not covered" > (c< / span > ) => < span class = "cstat-no" title = "statement not covered" > !c)< / span > }< / span >
inspectorWidth={layout.inspectorWidth}
onInspectorResize={layout.handleInspectorResize}
2026-02-22 11:38:03 +01:00
inspectorEntry={activeTab?.entry ?? null}
inspectorContent={activeTab?.content ?? null}
allContent={vault.allContent}
gitHistory={gitHistory}
onUpdateFrontmatter={notes.handleUpdateFrontmatter}
onDeleteProperty={notes.handleDeleteProperty}
onAddProperty={notes.handleAddProperty}
2026-02-25 21:35:30 +01:00
showAIChat={dialogs.showAIChat}
onToggleAIChat={dialogs.toggleAIChat}
vaultPath={vaultSwitcher.vaultPath}
onTrashNote={entryActions.handleTrashNote}
onRestoreNote={entryActions.handleRestoreNote}
onArchiveNote={entryActions.handleArchiveNote}
onUnarchiveNote={entryActions.handleUnarchiveNote}
onRenameTab={handleRenameTab}
onContentChange={handleContentChange}
canGoBack={navHistory.canGoBack}
canGoForward={navHistory.canGoForward}
onGoBack={handleGoBack}
onGoForward={handleGoForward}
2026-02-22 11:38:03 +01:00
/>
< /div>
< /div>
2026-02-25 21:35:30 +01:00
< StatusBar noteCount={vault.entries.length} modifiedCount={vault.modifiedFiles.length} vaultPath={vaultSwitcher.vaultPath} vaults={vaultSwitcher.allVaults} onSwitchVault={vaultSwitcher.switchVault} onOpenSettings={dialogs.openSettings} onOpenLocalFolder={vaultSwitcher.handleOpenLocalFolder} onConnectGitHub={dialogs.openGitHubVault} onClickPending={< span class = "fstat-no" title = "function not covered" > () => < span class = "cstat-no" title = "statement not covered" > s< / span > etSelection({ kind: 'filter', filter: 'changes' })} h< / span > asGitHub={!!settings.github_token} />
2026-02-22 11:38:03 +01:00
< Toast message={toastMessage} onDismiss={< span class = "fstat-no" title = "function not covered" > () => < span class = "cstat-no" title = "statement not covered" > s< / span > etToastMessage(null)} /< / span > >
2026-02-25 21:35:30 +01:00
< QuickOpenPalette open={dialogs.showQuickOpen} entries={vault.entries} onSelect={notes.handleSelectNote} onClose={dialogs.closeQuickOpen} />
< CommandPalette open={dialogs.showCommandPalette} commands={commands} onClose={dialogs.closeCommandPalette} />
< SearchPanel open={dialogs.showSearch} vaultPath={vaultSwitcher.vaultPath} entries={vault.entries} onSelectNote={notes.handleSelectNote} onClose={dialogs.closeSearch} />
< CreateTypeDialog open={dialogs.showCreateTypeDialog} onClose={dialogs.closeCreateType} onCreate={handleCreateType} />
< CommitDialog open={commitFlow.showCommitDialog} modifiedCount={vault.modifiedFiles.length} onCommit={commitFlow.handleCommitPush} onClose={commitFlow.closeCommitDialog} />
< SettingsPanel open={dialogs.showSettings} settings={settings} onSave={saveSettings} onClose={dialogs.closeSettings} />
< GitHubVaultModal
open={dialogs.showGitHubVault}
githubToken={settings.github_token}
onClose={dialogs.closeGitHubVault}
onVaultCloned={vaultSwitcher.handleVaultCloned}
onOpenSettings={< span class = "fstat-no" title = "function not covered" > () => {< / span > < span class = "cstat-no" title = "statement not covered" > dialogs.closeGitHubVault(); < span class = "cstat-no" title = "statement not covered" > d< / span > ialogs.openSettings() }< / span > }
2026-02-22 11:38:03 +01:00
/>
< /div>
)
}
export default App
< / pre > < / td > < / tr > < / table > < / pre >
< div class = 'push' > < / div > <!-- for sticky footer -->
< / div > <!-- /wrapper -->
< div class = 'footer quiet pad2 space-top1 center small' >
Code coverage generated by
< a href = "https://istanbul.js.org/" target = "_blank" rel = "noopener noreferrer" > istanbul< / a >
2026-02-25 21:35:30 +01:00
at 2026-02-25T20:19:25.575Z
2026-02-22 11:38:03 +01:00
< / div >
< script src = "../prettify.js" > < / script >
< script >
window.onload = function () {
prettyPrint();
};
< / script >
< script src = "../sorter.js" > < / script >
< script src = "../block-navigation.js" > < / script >
< / body >
< / html >