2026-02-22 11:38:03 +01:00
<!doctype html>
< html lang = "en" >
< head >
< title > Code coverage report for src/hooks/useKeyboardNavigation.ts< / 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/hooks< / a > useKeyboardNavigation.ts< / h1 >
< div class = 'clearfix' >
< div class = 'fl pad1y space-right2' >
2026-02-25 21:35:30 +01:00
< span class = "strong" > 94.82% < / 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' > 55/58< / 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" > 85.36% < / 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' > 35/41< / 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" > 93.33% < / 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' > 14/15< / 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" > 95.74% < / 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' > 45/47< / 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 >
2026-02-25 21:35:30 +01:00
< div class = 'status-line high' > < / div >
2026-02-22 11:38:03 +01:00
< 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 >
2026-02-25 21:35:30 +01:00
< a name = 'L124' > < / a > < a href = '#L124' > 124< / a > < / td > < td class = "line-coverage quiet" > < 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 >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 39x< / 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" > 39x< / 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 >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 4x< / span >
< span class = "cline-any cline-yes" > 4x< / 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" > 3x< / span >
< span class = "cline-any cline-yes" > 6x< / span >
< span class = "cline-any cline-yes" > 3x< / span >
< span class = "cline-any cline-yes" > 3x< / 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 >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 3x< / span >
< span class = "cline-any cline-yes" > 3x< / 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" > 3x< / span >
< span class = "cline-any cline-yes" > 6x< / 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" > 3x< / 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" > 3x< / span >
< span class = "cline-any cline-yes" > 3x< / span >
< span class = "cline-any cline-yes" > 2x< / 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" > 1x< / 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 >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 13x< / span >
< span class = "cline-any cline-yes" > 13x< / span >
< span class = "cline-any cline-yes" > 12x< / span >
< span class = "cline-any cline-yes" > 13x< / span >
< span class = "cline-any cline-yes" > 8x< / span >
< span class = "cline-any cline-yes" > 5x< / 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" > 7x< / 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" > 264x< / span >
< span class = "cline-any cline-yes" > 264x< / span >
< span class = "cline-any cline-yes" > 264x< / 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 >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 44x< / span >
< span class = "cline-any cline-yes" > 39x< / 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" > 44x< / span >
< span class = "cline-any cline-yes" > 44x< / span >
< span class = "cline-any cline-yes" > 44x< / span >
< span class = "cline-any cline-yes" > 44x< / span >
< span class = "cline-any cline-yes" > 44x< / span >
< span class = "cline-any cline-yes" > 44x< / 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" > 44x< / span >
< span class = "cline-any cline-yes" > 19x< / span >
< span class = "cline-any cline-yes" > 19x< / span >
< span class = "cline-any cline-yes" > 13x< / span >
< span class = "cline-any cline-yes" > 13x< / span >
2026-02-22 11:38:03 +01:00
< span class = "cline-any cline-yes" > 7x< / span >
< span class = "cline-any cline-yes" > 7x< / span >
2026-02-25 21:35:30 +01:00
< span class = "cline-any cline-yes" > 3x< / span >
< span class = "cline-any cline-neutral" > < / span >
< span class = "cline-any cline-yes" > 19x< / span >
< span class = "cline-any cline-yes" > 19x< / 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 > < / td > < td class = "text" > < pre class = "prettyprint lang-js" > import { useEffect, useMemo, useRef } from 'react'
import { isTauri } from '../mock-tauri'
2026-02-25 21:35:30 +01:00
import { filterEntries, sortByModified, buildRelationshipGroups } from '../utils/noteListHelpers'
2026-02-22 11:38:03 +01:00
import type { VaultEntry, SidebarSelection } from '../types'
interface Tab {
entry: VaultEntry
content: string
}
interface KeyboardNavigationOptions {
tabs: Tab[]
activeTabPath: string | null
entries: VaultEntry[]
selection: SidebarSelection
allContent: Record< string, string>
onSwitchTab: (path: string) => void
onReplaceActiveTab: (entry: VaultEntry) => void
onSelectNote: (entry: VaultEntry) => void
}
function computeVisibleNotes(
entries: VaultEntry[],
selection: SidebarSelection,
allContent: Record< string, string> ,
): VaultEntry[] {
< span class = "missing-if-branch" title = "if path not taken" > I< / span > if (selection.kind === 'entity') {
< span class = "cstat-no" title = "statement not covered" > return buildRelationshipGroups(selection.entry, entries, allContent)< / span >
.flatMap(< span class = "fstat-no" title = "function not covered" > (g< / span > ) => < span class = "cstat-no" title = "statement not covered" > g.entries)< / span >
}
return [...filterEntries(entries, selection)].sort(sortByModified)
}
2026-02-25 21:35:30 +01:00
function navigateTab(
2026-02-22 11:38:03 +01:00
tabsRef: React.RefObject< Tab[]> ,
activeTabPathRef: React.RefObject< string | null> ,
onSwitchTab: React.RefObject< (path: string) => void> ,
direction: 1 | -1,
) {
2026-02-25 21:35:30 +01:00
const currentTabs = tabsRef.current!
if (currentTabs.length === 0) return
2026-02-22 11:38:03 +01:00
2026-02-25 21:35:30 +01:00
const currentPath = activeTabPathRef.current
const currentIndex = currentTabs.findIndex((t) => t.entry.path === currentPath)
const nextIndex = (currentIndex + direction + currentTabs.length) % currentTabs.length
onSwitchTab.current!(currentTabs[nextIndex].entry.path)
2026-02-22 11:38:03 +01:00
}
2026-02-25 21:35:30 +01:00
function navigateNote(
2026-02-22 11:38:03 +01:00
visibleNotesRef: React.RefObject< VaultEntry[]> ,
activeTabPathRef: React.RefObject< string | null> ,
onReplace: React.RefObject< (entry: VaultEntry) => void> ,
onSelect: React.RefObject< (entry: VaultEntry) => void> ,
direction: 1 | -1,
) {
2026-02-25 21:35:30 +01:00
const notes = visibleNotesRef.current!
< span class = "missing-if-branch" title = "if path not taken" > I< / span > if (notes.length === 0) < span class = "cstat-no" title = "statement not covered" > return< / span >
2026-02-22 11:38:03 +01:00
2026-02-25 21:35:30 +01:00
const currentPath = activeTabPathRef.current
const currentIndex = notes.findIndex((n) => n.path === currentPath)
2026-02-22 11:38:03 +01:00
2026-02-25 21:35:30 +01:00
const nextIndex = currentIndex === -1
? (direction === 1 ? 0 : < span class = "branch-1 cbranch-no" title = "branch not covered" > notes.length - 1)< / span >
2026-02-22 11:38:03 +01:00
: (currentIndex + direction + notes.length) % notes.length
2026-02-25 21:35:30 +01:00
const nextNote = notes[nextIndex]
if (currentPath) {
onReplace.current!(nextNote)
2026-02-22 11:38:03 +01:00
} else {
2026-02-25 21:35:30 +01:00
onSelect.current!(nextNote)
2026-02-22 11:38:03 +01:00
}
}
2026-02-25 21:35:30 +01:00
type ShortcutKind = 'tab' | 'note' | null
function classifyShortcut(e: KeyboardEvent, inTauri: boolean): ShortcutKind {
const mod = e.metaKey || e.ctrlKey
if (!mod) return null
const isTabShortcut = inTauri ? (< span class = "branch-0 cbranch-no" title = "branch not covered" > e.altKey & & !e.shiftKey) : (e< / span > .shiftKey & & !e.altKey)
if (isTabShortcut & & (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) return 'tab'
if (e.altKey & & !e.shiftKey & & (e.key === 'ArrowUp' || e.key === 'ArrowDown')) return 'note'
return null
}
function arrowDirection(key: string): 1 | -1 {
return (key === 'ArrowRight' || key === 'ArrowDown') ? 1 : -1
}
function useLatestRef< T> (value: T): React.RefObject< T> {
const ref = useRef(value)
useEffect(() => { ref.current = value })
return ref
}
2026-02-22 11:38:03 +01:00
export function useKeyboardNavigation({
2026-02-25 21:35:30 +01:00
tabs, activeTabPath, entries, selection, allContent,
onSwitchTab, onReplaceActiveTab, onSelectNote,
2026-02-22 11:38:03 +01:00
}: KeyboardNavigationOptions) {
const visibleNotes = useMemo(
() => computeVisibleNotes(entries, selection, allContent),
[entries, selection, allContent],
)
2026-02-25 21:35:30 +01:00
const tabsRef = useLatestRef(tabs)
const activeTabPathRef = useLatestRef(activeTabPath)
const visibleNotesRef = useLatestRef(visibleNotes)
const onSwitchTabRef = useLatestRef(onSwitchTab)
const onReplaceRef = useLatestRef(onReplaceActiveTab)
const onSelectNoteRef = useLatestRef(onSelectNote)
2026-02-22 11:38:03 +01:00
useEffect(() => {
2026-02-25 21:35:30 +01:00
const inTauri = isTauri()
2026-02-22 11:38:03 +01:00
const handleKeyDown = (e: KeyboardEvent) => {
2026-02-25 21:35:30 +01:00
const kind = classifyShortcut(e, inTauri)
if (!kind) return
e.preventDefault()
if (kind === 'tab') navigateTab(tabsRef, activeTabPathRef, onSwitchTabRef, arrowDirection(e.key))
else navigateNote(visibleNotesRef, activeTabPathRef, onReplaceRef, onSelectNoteRef, arrowDirection(e.key))
2026-02-22 11:38:03 +01:00
}
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
2026-02-25 21:35:30 +01:00
}, [tabsRef, activeTabPathRef, visibleNotesRef, onSwitchTabRef, onReplaceRef, onSelectNoteRef])
2026-02-22 11:38:03 +01:00
}
< / 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 >