feat: unified search panel — progressive keyword+semantic results (#80)
* feat: unify search panel — remove keyword/semantic toggle, progressive results Replace separate keyword/semantic mode toggle with a unified search that streams results progressively: keyword results appear first (<500ms), then hybrid results augment the list (~1-2s). Loading spinner shows in the input field while semantic search runs. Stale requests are discarded via generation counter for safe rapid typing. - Extract search logic into useUnifiedSearch hook (code health 9.26+) - 300ms debounce, 5s timeout on hybrid, graceful fallback on errors - 15 tests covering progressive search, spinner, stale results, failures Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add design wireframes for unified search panel Two frames showing the search panel states: - "keyword results loaded, semantic loading" (with spinner) - "all results loaded" (no spinner, more results including semantic) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: coverage artifacts --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
Task: relations-type-color
|
||||
Summary: Fixed relation chips showing default blue instead of type color. Root cause: resolveRef matched entries by path/filename only, while wiki-links used findEntryByTarget (title, filename, aliases). Fixed by adding title/alias matching to resolveRef, changing default color from blue to neutral grey for typeless notes, and passing custom color keys to TypeSelector. Also refactored DynamicPropertiesPanel to reduce cyclomatic complexity (11→10). All tests pass (739), code health gates pass, coverage 79%.
|
||||
Task: Search panel unify
|
||||
Summary: Removed keyword/semantic toggle from search panel. Search now uses a two-phase approach: keyword results appear first (<500ms), then hybrid/semantic results blend in (~1-2s). Added loading spinner in input field during search. Implemented 300ms debounce and generation counter to safely handle rapid typing (no crashes). Extracted search logic into useUnifiedSearch hook for code health (9.26+). 15 tests covering progressive search, spinner, stale result handling, and hybrid failure fallback. Created design wireframes in design/search-panel-unify.pen.
|
||||
Commits: 2
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">45.44% </span>
|
||||
<span class="strong">79.25% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>1003/2207</span>
|
||||
<span class='fraction'>2819/3557</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">44.25% </span>
|
||||
<span class="strong">72.94% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>651/1471</span>
|
||||
<span class='fraction'>1577/2162</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">40.81% </span>
|
||||
<span class="strong">75.78% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>269/659</span>
|
||||
<span class='fraction'>848/1119</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">47.64% </span>
|
||||
<span class="strong">81.24% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>911/1912</span>
|
||||
<span class='fraction'>2374/2922</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line medium'></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
@@ -80,62 +80,62 @@
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td class="file low" data-value="src"><a href="src/index.html">src</a></td>
|
||||
<td data-value="40.9" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 40%"></div><div class="cover-empty" style="width: 60%"></div></div>
|
||||
<td data-value="47.5" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 47%"></div><div class="cover-empty" style="width: 53%"></div></div>
|
||||
</td>
|
||||
<td data-value="40.9" class="pct low">40.9%</td>
|
||||
<td data-value="110" class="abs low">45/110</td>
|
||||
<td data-value="50" class="pct medium">50%</td>
|
||||
<td data-value="22" class="abs medium">11/22</td>
|
||||
<td data-value="13.51" class="pct low">13.51%</td>
|
||||
<td data-value="37" class="abs low">5/37</td>
|
||||
<td data-value="45" class="pct low">45%</td>
|
||||
<td data-value="100" class="abs low">45/100</td>
|
||||
<td data-value="47.5" class="pct low">47.5%</td>
|
||||
<td data-value="120" class="abs low">57/120</td>
|
||||
<td data-value="40.47" class="pct low">40.47%</td>
|
||||
<td data-value="42" class="abs low">17/42</td>
|
||||
<td data-value="20.58" class="pct low">20.58%</td>
|
||||
<td data-value="34" class="abs low">7/34</td>
|
||||
<td data-value="57.73" class="pct medium">57.73%</td>
|
||||
<td data-value="97" class="abs medium">56/97</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="src/components"><a href="src/components/index.html">src/components</a></td>
|
||||
<td data-value="54.44" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 54%"></div><div class="cover-empty" style="width: 46%"></div></div>
|
||||
<td class="file high" data-value="src/components"><a href="src/components/index.html">src/components</a></td>
|
||||
<td data-value="80.93" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 80%"></div><div class="cover-empty" style="width: 20%"></div></div>
|
||||
</td>
|
||||
<td data-value="54.44" class="pct medium">54.44%</td>
|
||||
<td data-value="1304" class="abs medium">710/1304</td>
|
||||
<td data-value="50.75" class="pct medium">50.75%</td>
|
||||
<td data-value="1064" class="abs medium">540/1064</td>
|
||||
<td data-value="50.11" class="pct medium">50.11%</td>
|
||||
<td data-value="419" class="abs medium">210/419</td>
|
||||
<td data-value="57.02" class="pct medium">57.02%</td>
|
||||
<td data-value="1103" class="abs medium">629/1103</td>
|
||||
<td data-value="80.93" class="pct high">80.93%</td>
|
||||
<td data-value="1831" class="abs high">1482/1831</td>
|
||||
<td data-value="73.93" class="pct medium">73.93%</td>
|
||||
<td data-value="1339" class="abs medium">990/1339</td>
|
||||
<td data-value="80.36" class="pct high">80.36%</td>
|
||||
<td data-value="611" class="abs high">491/611</td>
|
||||
<td data-value="82.93" class="pct high">82.93%</td>
|
||||
<td data-value="1506" class="abs high">1249/1506</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="src/components/ui"><a href="src/components/ui/index.html">src/components/ui</a></td>
|
||||
<td data-value="19.67" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 19%"></div><div class="cover-empty" style="width: 81%"></div></div>
|
||||
<td class="file high" data-value="src/components/ui"><a href="src/components/ui/index.html">src/components/ui</a></td>
|
||||
<td data-value="88.23" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 88%"></div><div class="cover-empty" style="width: 12%"></div></div>
|
||||
</td>
|
||||
<td data-value="19.67" class="pct low">19.67%</td>
|
||||
<td data-value="61" class="abs low">12/61</td>
|
||||
<td data-value="22.85" class="pct low">22.85%</td>
|
||||
<td data-value="35" class="abs low">8/35</td>
|
||||
<td data-value="16.07" class="pct low">16.07%</td>
|
||||
<td data-value="56" class="abs low">9/56</td>
|
||||
<td data-value="19.67" class="pct low">19.67%</td>
|
||||
<td data-value="61" class="abs low">12/61</td>
|
||||
<td data-value="88.23" class="pct high">88.23%</td>
|
||||
<td data-value="17" class="abs high">15/17</td>
|
||||
<td data-value="80" class="pct high">80%</td>
|
||||
<td data-value="15" class="abs high">12/15</td>
|
||||
<td data-value="84.61" class="pct high">84.61%</td>
|
||||
<td data-value="13" class="abs high">11/13</td>
|
||||
<td data-value="88.23" class="pct high">88.23%</td>
|
||||
<td data-value="17" class="abs high">15/17</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="src/hooks"><a href="src/hooks/index.html">src/hooks</a></td>
|
||||
<td data-value="27.51" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 27%"></div><div class="cover-empty" style="width: 73%"></div></div>
|
||||
<td class="file high" data-value="src/hooks"><a href="src/hooks/index.html">src/hooks</a></td>
|
||||
<td data-value="85.11" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 85%"></div><div class="cover-empty" style="width: 15%"></div></div>
|
||||
</td>
|
||||
<td data-value="27.51" class="pct low">27.51%</td>
|
||||
<td data-value="556" class="abs low">153/556</td>
|
||||
<td data-value="16.45" class="pct low">16.45%</td>
|
||||
<td data-value="231" class="abs low">38/231</td>
|
||||
<td data-value="26.01" class="pct low">26.01%</td>
|
||||
<td data-value="123" class="abs low">32/123</td>
|
||||
<td data-value="29.77" class="pct low">29.77%</td>
|
||||
<td data-value="487" class="abs low">145/487</td>
|
||||
<td data-value="85.11" class="pct high">85.11%</td>
|
||||
<td data-value="1075" class="abs high">915/1075</td>
|
||||
<td data-value="78.01" class="pct medium">78.01%</td>
|
||||
<td data-value="414" class="abs medium">323/414</td>
|
||||
<td data-value="77.91" class="pct medium">77.91%</td>
|
||||
<td data-value="326" class="abs medium">254/326</td>
|
||||
<td data-value="87.99" class="pct high">87.99%</td>
|
||||
<td data-value="866" class="abs high">762/866</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -154,18 +154,33 @@
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="src/utils"><a href="src/utils/index.html">src/utils</a></td>
|
||||
<td data-value="46.85" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 46%"></div><div class="cover-empty" style="width: 54%"></div></div>
|
||||
<td class="file low" data-value="src/mock-tauri"><a href="src/mock-tauri/index.html">src/mock-tauri</a></td>
|
||||
<td data-value="17.68" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 17%"></div><div class="cover-empty" style="width: 83%"></div></div>
|
||||
</td>
|
||||
<td data-value="46.85" class="pct low">46.85%</td>
|
||||
<td data-value="175" class="abs low">82/175</td>
|
||||
<td data-value="45.37" class="pct low">45.37%</td>
|
||||
<td data-value="119" class="abs low">54/119</td>
|
||||
<td data-value="52.17" class="pct medium">52.17%</td>
|
||||
<td data-value="23" class="abs medium">12/23</td>
|
||||
<td data-value="49.37" class="pct low">49.37%</td>
|
||||
<td data-value="160" class="abs low">79/160</td>
|
||||
<td data-value="17.68" class="pct low">17.68%</td>
|
||||
<td data-value="164" class="abs low">29/164</td>
|
||||
<td data-value="10.11" class="pct low">10.11%</td>
|
||||
<td data-value="89" class="abs low">9/89</td>
|
||||
<td data-value="2.08" class="pct low">2.08%</td>
|
||||
<td data-value="48" class="abs low">1/48</td>
|
||||
<td data-value="18.54" class="pct low">18.54%</td>
|
||||
<td data-value="151" class="abs low">28/151</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="src/utils"><a href="src/utils/index.html">src/utils</a></td>
|
||||
<td data-value="91.69" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 91%"></div><div class="cover-empty" style="width: 9%"></div></div>
|
||||
</td>
|
||||
<td data-value="91.69" class="pct high">91.69%</td>
|
||||
<td data-value="349" class="abs high">320/349</td>
|
||||
<td data-value="85.93" class="pct high">85.93%</td>
|
||||
<td data-value="263" class="abs high">226/263</td>
|
||||
<td data-value="96.51" class="pct high">96.51%</td>
|
||||
<td data-value="86" class="abs high">83/86</td>
|
||||
<td data-value="92.6" class="pct high">92.6%</td>
|
||||
<td data-value="284" class="abs high">263/284</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
@@ -176,7 +191,7 @@
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">40.9% </span>
|
||||
<span class="strong">47.5% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>45/110</span>
|
||||
<span class='fraction'>57/120</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">50% </span>
|
||||
<span class="strong">40.47% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>11/22</span>
|
||||
<span class='fraction'>17/42</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">13.51% </span>
|
||||
<span class="strong">20.58% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>5/37</span>
|
||||
<span class='fraction'>7/34</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">45% </span>
|
||||
<span class="strong">57.73% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>45/100</span>
|
||||
<span class='fraction'>56/97</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -336,7 +336,50 @@
|
||||
<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>
|
||||
<a name='L274'></a><a href='#L274'>274</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<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>
|
||||
<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>
|
||||
@@ -365,8 +408,14 @@
|
||||
<span class="cline-any cline-yes">1x</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-yes">1x</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>
|
||||
<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-neutral"> </span>
|
||||
@@ -375,82 +424,18 @@
|
||||
<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">1x</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-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">21x</span>
|
||||
<span class="cline-any cline-yes">14x</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-yes">22x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </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>
|
||||
<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">22x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</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-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</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>
|
||||
<span class="cline-any cline-yes">22x</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-yes">22x</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>
|
||||
<span class="cline-any cline-yes">22x</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-no"> </span>
|
||||
<span class="cline-any cline-no"> </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-yes">22x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
@@ -458,26 +443,83 @@
|
||||
<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-yes">22x</span>
|
||||
<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-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-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-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-neutral"> </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>
|
||||
<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>
|
||||
<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">10x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">10x</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-neutral"> </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-no"> </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-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </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-no"> </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-yes">22x</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-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-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</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>
|
||||
<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-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<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-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>
|
||||
@@ -485,55 +527,51 @@
|
||||
<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-yes">35x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-no"> </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-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</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-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-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-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-yes">35x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</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-yes">22x</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-yes">22x</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-yes">22x</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-yes">22x</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-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-neutral"> </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-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>
|
||||
<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-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -545,6 +583,31 @@
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
@@ -569,7 +632,25 @@
|
||||
<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-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>
|
||||
@@ -584,48 +665,39 @@
|
||||
<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-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-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-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></td><td class="text"><pre class="prettyprint lang-js">import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import { Sidebar } from './components/Sidebar'
|
||||
import { NoteList } from './components/NoteList'
|
||||
import { Editor } from './components/Editor'
|
||||
import { ResizeHandle } from './components/ResizeHandle'
|
||||
import { CreateNoteDialog } from './components/CreateNoteDialog'
|
||||
import { CreateTypeDialog } from './components/CreateTypeDialog'
|
||||
import { QuickOpenPalette } from './components/QuickOpenPalette'
|
||||
import { CommandPalette } from './components/CommandPalette'
|
||||
import { SearchPanel } from './components/SearchPanel'
|
||||
import { Toast } from './components/Toast'
|
||||
import { CommitDialog } from './components/CommitDialog'
|
||||
import { StatusBar } from './components/StatusBar'
|
||||
import { SettingsPanel } from './components/SettingsPanel'
|
||||
import { GitHubVaultModal } from './components/GitHubVaultModal'
|
||||
import { useVaultLoader } from './hooks/useVaultLoader'
|
||||
import { useSettings } from './hooks/useSettings'
|
||||
import { useNoteActions } from './hooks/useNoteActions'
|
||||
import { useAppKeyboard } from './hooks/useAppKeyboard'
|
||||
import { isTauri } from './mock-tauri'
|
||||
import { useKeyboardNavigation } from './hooks/useKeyboardNavigation'
|
||||
import type { SidebarSelection, GitCommit } from './types'
|
||||
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'
|
||||
import './App.css'
|
||||
|
||||
// Type declaration for mock content storage
|
||||
@@ -637,186 +709,206 @@ declare global {
|
||||
|
||||
const DEFAULT_SELECTION: SidebarSelection = { kind: 'filter', filter: 'all' }
|
||||
|
||||
// In web/browser mode: only Demo v2 (no real vault access)
|
||||
// In native Tauri mode: Demo v2 + real Laputa vault
|
||||
const VAULTS = isTauri()
|
||||
? <span class="branch-0 cbranch-no" title="branch not covered" >[</span>
|
||||
{ label: 'Demo v2', path: '/Users/luca/Workspace/laputa-app/demo-vault-v2' },
|
||||
{ label: 'Laputa', path: '/Users/luca/Laputa' },
|
||||
]
|
||||
: [
|
||||
{ label: 'Demo v2', path: '/Users/luca/Workspace/laputa-app/demo-vault-v2' },
|
||||
]
|
||||
|
||||
const BUILT_IN_TYPE_NAMES = new Set([
|
||||
'Project', 'Experiment', 'Responsibility', 'Procedure',
|
||||
'Person', 'Event', 'Topic', 'Type', 'Note', 'Essay',
|
||||
'Quarter', 'Journal', 'Evergreen',
|
||||
])
|
||||
|
||||
function App() {
|
||||
const [selection, setSelection] = useState<SidebarSelection>(DEFAULT_SELECTION)
|
||||
function useLayoutPanels() {
|
||||
const [sidebarWidth, setSidebarWidth] = useState(250)
|
||||
const [noteListWidth, setNoteListWidth] = useState(300)
|
||||
const [inspectorWidth, setInspectorWidth] = useState(280)
|
||||
const [inspectorCollapsed, setInspectorCollapsed] = useState(false)
|
||||
const [gitHistory, setGitHistory] = useState<GitCommit[]>([])
|
||||
const [showCreateDialog, setShowCreateDialog] = useState(false)
|
||||
const [createNoteDefaultType, setCreateNoteDefaultType] = useState<string | undefined>()
|
||||
const [showCreateTypeDialog, setShowCreateTypeDialog] = useState(false)
|
||||
const [showQuickOpen, setShowQuickOpen] = useState(false)
|
||||
const [showCommitDialog, setShowCommitDialog] = useState(false)
|
||||
const [toastMessage, setToastMessage] = useState<string | null>(null)
|
||||
const [vaultPath, setVaultPath] = useState(VAULTS[0].path)
|
||||
const [showAIChat, setShowAIChat] = useState(false)
|
||||
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 }
|
||||
}
|
||||
|
||||
const vault = useVaultLoader(vaultPath)
|
||||
const notes = useNoteActions(vault.addEntry, vault.updateContent, vault.entries, setToastMessage)
|
||||
|
||||
// Derive custom types from vault (Type entries not in built-in list)
|
||||
const customTypes = useMemo(
|
||||
() => vault.entries
|
||||
.filter((e) => e.isA === 'Type' && <span class="branch-1 cbranch-no" title="branch not covered" >!BUILT_IN_TYPE_NAMES.has(e.title))</span>
|
||||
.map(<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >e.title)</span>
|
||||
.sort(),
|
||||
[vault.entries],
|
||||
)
|
||||
|
||||
// Reset UI state when vault changes
|
||||
const handleSwitchVault = useCallback(<span class="fstat-no" title="function not covered" >(p</span>ath: string) => {
|
||||
<span class="cstat-no" title="statement not covered" > setVaultPath(path)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setSelection(DEFAULT_SELECTION)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setGitHistory([])</span>
|
||||
<span class="cstat-no" title="statement not covered" > notes.closeAllTabs()</span>
|
||||
}, [notes])
|
||||
|
||||
// Load git history when active tab changes
|
||||
useEffect(() => {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (!notes.activeTabPath) {
|
||||
setGitHistory([])
|
||||
return
|
||||
/** 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>
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > vault.loadGitHistory(notes.activeTabPath).then(setGitHistory)</span>
|
||||
}, [notes.activeTabPath, vault.loadGitHistory])
|
||||
}, [rawOnChange, updateEntry])
|
||||
return { ...editor, handleContentChange }
|
||||
}
|
||||
|
||||
const openCreateDialog = useCallback(<span class="fstat-no" title="function not covered" >(t</span>ype?: string) => {
|
||||
<span class="cstat-no" title="statement not covered" > setCreateNoteDefaultType(type)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setShowCreateDialog(true)</span>
|
||||
}, [])
|
||||
function App() {
|
||||
const [selection, setSelection] = useState<SidebarSelection>(DEFAULT_SELECTION)
|
||||
const layout = useLayoutPanels()
|
||||
const [toastMessage, setToastMessage] = useState<string | null>(null)
|
||||
const dialogs = useDialogs()
|
||||
|
||||
const openCreateTypeDialog = useCallback(<span class="fstat-no" title="function not covered" >() => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > setShowCreateTypeDialog(true)</span>
|
||||
}, [])
|
||||
// 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])
|
||||
|
||||
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)
|
||||
useEffect(() => {
|
||||
<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>
|
||||
}
|
||||
navFromHistoryRef.current = false
|
||||
}, [notes.activeTabPath]) // eslint-disable-line react-hooks/exhaustive-deps -- navHistory.push is stable
|
||||
|
||||
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])
|
||||
|
||||
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)
|
||||
|
||||
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>
|
||||
}, [notes, setToastMessage])
|
||||
}, [notes])
|
||||
|
||||
const handleCustomizeType = useCallback(<span class="fstat-no" title="function not covered" >(t</span>ypeName: string, icon: string, color: string) => {
|
||||
const typeEntry = <span class="cstat-no" title="statement not covered" >vault.entries.find(<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >e.isA === 'Type' && e.title === typeName)</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > if (!typeEntry) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
// Update icon and color in frontmatter (two separate calls)
|
||||
<span class="cstat-no" title="statement not covered" > notes.handleUpdateFrontmatter(typeEntry.path, 'icon', icon)</span>
|
||||
<span class="cstat-no" title="statement not covered" > notes.handleUpdateFrontmatter(typeEntry.path, 'color', color)</span>
|
||||
// Also update the entry in-memory for instant UI feedback
|
||||
<span class="cstat-no" title="statement not covered" > vault.updateEntry(typeEntry.path, { icon, color })</span>
|
||||
}, [vault, 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])
|
||||
|
||||
const handleTrashNote = useCallback(<span class="fstat-no" title="function not covered" >async (p</span>ath: string) => {
|
||||
const <span class="cstat-no" title="statement not covered" >now = new Date().toISOString().slice(0, 10)</span>
|
||||
<span class="cstat-no" title="statement not covered" > await notes.handleUpdateFrontmatter(path, 'trashed', true)</span>
|
||||
<span class="cstat-no" title="statement not covered" > await notes.handleUpdateFrontmatter(path, 'trashed_at', now)</span>
|
||||
<span class="cstat-no" title="statement not covered" > vault.updateEntry(path, { trashed: true, trashedAt: Date.now() / 1000 })</span>
|
||||
<span class="cstat-no" title="statement not covered" > setToastMessage('Note moved to trash')</span>
|
||||
}, [notes, vault, setToastMessage])
|
||||
const { setViewMode, sidebarVisible, noteListVisible } = useViewMode()
|
||||
|
||||
const handleRestoreNote = useCallback(<span class="fstat-no" title="function not covered" >async (p</span>ath: string) => {
|
||||
<span class="cstat-no" title="statement not covered" > await notes.handleUpdateFrontmatter(path, 'trashed', false)</span>
|
||||
<span class="cstat-no" title="statement not covered" > await notes.handleDeleteProperty(path, 'trashed_at')</span>
|
||||
<span class="cstat-no" title="statement not covered" > vault.updateEntry(path, { trashed: false, trashedAt: null })</span>
|
||||
<span class="cstat-no" title="statement not covered" > setToastMessage('Note restored from trash')</span>
|
||||
}, [notes, vault, setToastMessage])
|
||||
|
||||
const handleArchiveNote = useCallback(<span class="fstat-no" title="function not covered" >async (p</span>ath: string) => {
|
||||
<span class="cstat-no" title="statement not covered" > await notes.handleUpdateFrontmatter(path, 'archived', true)</span>
|
||||
<span class="cstat-no" title="statement not covered" > vault.updateEntry(path, { archived: true })</span>
|
||||
<span class="cstat-no" title="statement not covered" > setToastMessage('Note archived')</span>
|
||||
}, [notes, vault, setToastMessage])
|
||||
|
||||
const handleUnarchiveNote = useCallback(<span class="fstat-no" title="function not covered" >async (p</span>ath: string) => {
|
||||
<span class="cstat-no" title="statement not covered" > await notes.handleUpdateFrontmatter(path, 'archived', false)</span>
|
||||
<span class="cstat-no" title="statement not covered" > vault.updateEntry(path, { archived: false })</span>
|
||||
<span class="cstat-no" title="statement not covered" > setToastMessage('Note unarchived')</span>
|
||||
}, [notes, vault, setToastMessage])
|
||||
|
||||
const handleReorderSections = useCallback(<span class="fstat-no" title="function not covered" >(o</span>rderedTypes: { typeName: string; order: number }[]) => {
|
||||
<span class="cstat-no" title="statement not covered" > for (const { typeName, order } of orderedTypes) {</span>
|
||||
const typeEntry = <span class="cstat-no" title="statement not covered" >vault.entries.find(<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >e.isA === 'Type' && e.title === typeName)</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > if (!typeEntry) <span class="cstat-no" title="statement not covered" >continue</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > notes.handleUpdateFrontmatter(typeEntry.path, 'order', order)</span>
|
||||
<span class="cstat-no" title="statement not covered" > vault.updateEntry(typeEntry.path, { order })</span>
|
||||
}
|
||||
}, [vault, notes])
|
||||
|
||||
useAppKeyboard({
|
||||
onQuickOpen: <span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etShowQuickOpen(true),</span>
|
||||
onCreateNote: openCreateDialog,
|
||||
onSave: () => setToastMessage('Saved'),
|
||||
onTrashNote: handleTrashNote,
|
||||
onArchiveNote: handleArchiveNote,
|
||||
activeTabPathRef: notes.activeTabPathRef,
|
||||
handleCloseTabRef: notes.handleCloseTabRef,
|
||||
})
|
||||
|
||||
useKeyboardNavigation({
|
||||
tabs: notes.tabs,
|
||||
activeTabPath: notes.activeTabPath,
|
||||
entries: vault.entries,
|
||||
selection,
|
||||
allContent: vault.allContent,
|
||||
onSwitchTab: notes.handleSwitchTab,
|
||||
onReplaceActiveTab: notes.handleReplaceActiveTab,
|
||||
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,
|
||||
onSelectNote: notes.handleSelectNote,
|
||||
onGoBack: handleGoBack, onGoForward: handleGoForward,
|
||||
canGoBack: navHistory.canGoBack, canGoForward: navHistory.canGoForward,
|
||||
})
|
||||
|
||||
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>
|
||||
}, [])
|
||||
|
||||
const handleCommitPush = useCallback(<span class="fstat-no" title="function not covered" >async (m</span>essage: string) => {
|
||||
<span class="cstat-no" title="statement not covered" > setShowCommitDialog(false)</span>
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
const result = <span class="cstat-no" title="statement not covered" >await vault.commitAndPush(message)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setToastMessage(result)</span>
|
||||
<span class="cstat-no" title="statement not covered" > vault.loadModifiedFiles()</span>
|
||||
} catch (err) {
|
||||
<span class="cstat-no" title="statement not covered" > console.error('Commit failed:', err)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setToastMessage(`Commit failed: ${err}`)</span>
|
||||
}
|
||||
}, [vault])
|
||||
const { status: updateStatus, actions: updateActions } = useUpdater()
|
||||
|
||||
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">
|
||||
<UpdateBanner status={updateStatus} actions={updateActions} />
|
||||
<div className="app">
|
||||
<div className="app__sidebar" style={{ width: sidebarWidth }}>
|
||||
<Sidebar entries={vault.entries} selection={selection} onSelect={setSelection} onSelectNote={notes.handleSelectNote} onCreateType={openCreateDialog} onCreateNewType={openCreateTypeDialog} onCustomizeType={handleCustomizeType} onReorderSections={handleReorderSections} modifiedCount={vault.modifiedFiles.length} onCommitPush={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etShowCommitDialog(true)} /</span>>
|
||||
</div>
|
||||
<ResizeHandle onResize={handleSidebarResize} />
|
||||
<div className="app__note-list" style={{ width: noteListWidth }}>
|
||||
<NoteList entries={vault.entries} selection={selection} selectedNote={activeTab?.entry ?? null} allContent={vault.allContent} modifiedFiles={vault.modifiedFiles} onSelectNote={notes.handleSelectNote} onCreateNote={openCreateDialog} />
|
||||
</div>
|
||||
<ResizeHandle onResize={handleNoteListResize} />
|
||||
{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} />
|
||||
</>
|
||||
)}
|
||||
<div className="app__editor">
|
||||
<Editor
|
||||
tabs={notes.tabs}
|
||||
@@ -828,12 +920,12 @@ function App() {
|
||||
onNavigateWikilink={notes.handleNavigateWikilink}
|
||||
onLoadDiff={vault.loadDiff}
|
||||
onLoadDiffAtCommit={vault.loadDiffAtCommit}
|
||||
isModified={vault.isFileModified}
|
||||
onCreateNote={openCreateDialog}
|
||||
inspectorCollapsed={inspectorCollapsed}
|
||||
onToggleInspector={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etInspectorCollapsed(<span class="fstat-no" title="function not covered" >(c</span>) => <span class="cstat-no" title="statement not covered" >!c)</span>}</span>
|
||||
inspectorWidth={inspectorWidth}
|
||||
onInspectorResize={handleInspectorResize}
|
||||
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}
|
||||
inspectorEntry={activeTab?.entry ?? null}
|
||||
inspectorContent={activeTab?.content ?? null}
|
||||
allContent={vault.allContent}
|
||||
@@ -841,41 +933,36 @@ function App() {
|
||||
onUpdateFrontmatter={notes.handleUpdateFrontmatter}
|
||||
onDeleteProperty={notes.handleDeleteProperty}
|
||||
onAddProperty={notes.handleAddProperty}
|
||||
showAIChat={showAIChat}
|
||||
onToggleAIChat={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etShowAIChat(<span class="fstat-no" title="function not covered" >c => <span class="cstat-no" title="statement not covered" >!</span>c)</span>}</span>
|
||||
vaultPath={vaultPath}
|
||||
onTrashNote={handleTrashNote}
|
||||
onRestoreNote={handleRestoreNote}
|
||||
onArchiveNote={handleArchiveNote}
|
||||
onUnarchiveNote={handleUnarchiveNote}
|
||||
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}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<StatusBar noteCount={vault.entries.length} vaultPath={vaultPath} vaults={VAULTS} onSwitchVault={handleSwitchVault} />
|
||||
<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} />
|
||||
<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>>
|
||||
<QuickOpenPalette
|
||||
open={showQuickOpen}
|
||||
entries={vault.entries}
|
||||
onSelect={notes.handleSelectNote}
|
||||
onClose={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etShowQuickOpen(false)}</span>
|
||||
/>
|
||||
<CreateNoteDialog
|
||||
open={showCreateDialog}
|
||||
onClose={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etShowCreateDialog(false)}</span>
|
||||
onCreate={notes.handleCreateNote}
|
||||
defaultType={createNoteDefaultType}
|
||||
customTypes={customTypes}
|
||||
/>
|
||||
<CreateTypeDialog
|
||||
open={showCreateTypeDialog}
|
||||
onClose={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etShowCreateTypeDialog(false)}</span>
|
||||
onCreate={handleCreateType}
|
||||
/>
|
||||
<CommitDialog
|
||||
open={showCommitDialog}
|
||||
modifiedCount={vault.modifiedFiles.length}
|
||||
onCommit={handleCommitPush}
|
||||
onClose={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etShowCommitDialog(false)}</span>
|
||||
<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>}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
@@ -889,7 +976,7 @@ export default App
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -30,9 +30,9 @@
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">69.23% </span>
|
||||
<span class="strong">71.42% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>18/26</span>
|
||||
<span class='fraction'>20/28</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -240,7 +240,13 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<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></td><td class="line-coverage quiet"><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>
|
||||
@@ -280,7 +286,7 @@
|
||||
<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">16x</span>
|
||||
<span class="cline-any cline-yes">30x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -387,7 +393,13 @@
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">30x</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>
|
||||
@@ -418,7 +430,7 @@
|
||||
<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 { memo } from 'react'
|
||||
import type { VaultEntry } from '../types'
|
||||
import type { VaultEntry, NoteStatus } from '../types'
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
MagnifyingGlass,
|
||||
@@ -436,7 +448,7 @@ import {
|
||||
interface BreadcrumbBarProps {
|
||||
entry: VaultEntry
|
||||
wordCount: number
|
||||
isModified: boolean
|
||||
noteStatus: NoteStatus
|
||||
showDiffToggle: boolean
|
||||
diffMode: boolean
|
||||
diffLoading: boolean
|
||||
@@ -456,7 +468,7 @@ const DISABLED_ICON_STYLE = { opacity: 0.4, cursor: 'not-allowed' } as const
|
||||
function BreadcrumbActions({ entry, showDiffToggle, diffMode, diffLoading, onToggleDiff,
|
||||
showAIChat, onToggleAIChat, inspectorCollapsed, onToggleInspector,
|
||||
onTrash, onRestore, onArchive, onUnarchive,
|
||||
}: Omit<BreadcrumbBarProps, 'wordCount' | 'isModified'>) {
|
||||
}: Omit<BreadcrumbBarProps, 'wordCount' | 'noteStatus'>) {
|
||||
return (
|
||||
<div className="flex items-center" style={{ gap: 12 }}>
|
||||
<button
|
||||
@@ -562,7 +574,7 @@ function BreadcrumbActions({ entry, showDiffToggle, diffMode, diffLoading, onTog
|
||||
}
|
||||
|
||||
export const BreadcrumbBar = memo(function BreadcrumbBar({
|
||||
entry, wordCount, isModified, ...actionProps
|
||||
entry, wordCount, noteStatus, ...actionProps
|
||||
}: BreadcrumbBarProps) {
|
||||
return (
|
||||
<div
|
||||
@@ -581,7 +593,13 @@ export const BreadcrumbBar = memo(function BreadcrumbBar({
|
||||
<span className="font-medium text-foreground">{entry.title}</span>
|
||||
<span className="text-muted-foreground" style={{ margin: '0 4px' }}>&middot;</span>
|
||||
<span className="text-muted-foreground">{wordCount.toLocaleString()} words</span>
|
||||
{isModified && (
|
||||
{noteStatus === 'new' && (
|
||||
<>
|
||||
<span className="text-muted-foreground" style={{ margin: '0 4px' }}>&middot;</span>
|
||||
<span className="font-semibold" style={{ color: 'var(--accent-green)' }}>N</span>
|
||||
</>
|
||||
)}
|
||||
{noteStatus === 'modified' && (
|
||||
<>
|
||||
<span className="text-muted-foreground" style={{ margin: '0 4px' }}>&middot;</span>
|
||||
<span className="font-semibold" style={{ color: 'var(--accent-yellow)' }}>M</span>
|
||||
@@ -601,7 +619,7 @@ export const BreadcrumbBar = memo(function BreadcrumbBar({
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">31.81% </span>
|
||||
<span class="strong">95.45% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>7/22</span>
|
||||
<span class='fraction'>21/22</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">13.33% </span>
|
||||
<span class="strong">73.33% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>2/15</span>
|
||||
<span class='fraction'>11/15</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">28.57% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>2/7</span>
|
||||
<span class='fraction'>7/7</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">36.84% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>7/19</span>
|
||||
<span class='fraction'>19/19</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -149,33 +149,33 @@
|
||||
<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">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">53x</span>
|
||||
<span class="cline-any cline-yes">53x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">53x</span>
|
||||
<span class="cline-any cline-yes">20x</span>
|
||||
<span class="cline-any cline-yes">9x</span>
|
||||
<span class="cline-any cline-yes">9x</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-yes">22x</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-yes">53x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</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-yes">53x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">22x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">53x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -190,7 +190,7 @@
|
||||
<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-yes">4x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -226,35 +226,35 @@ export function CommitDialog({ open, modifiedCount, onCommit, onClose }: CommitD
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (open) {
|
||||
<span class="cstat-no" title="statement not covered" > setMessage('')</span>
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >i</span>nputRef.current?.focus(), 5</span>0)</span>
|
||||
if (open) {
|
||||
setMessage('') // eslint-disable-line react-hooks/set-state-in-effect -- reset on dialog open
|
||||
setTimeout(() => inputRef.current?.focus(), 50)
|
||||
}
|
||||
}, [open])
|
||||
|
||||
const handleSubmit = <span class="fstat-no" title="function not covered" >() => {</span>
|
||||
const trimmed = <span class="cstat-no" title="statement not covered" >message.trim()</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (!trimmed) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > onCommit(trimmed)</span>
|
||||
const handleSubmit = () => {
|
||||
const trimmed = message.trim()
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (!trimmed) <span class="cstat-no" title="statement not covered" >return</span>
|
||||
onCommit(trimmed)
|
||||
}
|
||||
|
||||
const handleKeyDown = <span class="fstat-no" title="function not covered" >(e</span>: React.KeyboardEvent) => {
|
||||
<span class="cstat-no" title="statement not covered" > if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > e.preventDefault()</span>
|
||||
<span class="cstat-no" title="statement not covered" > handleSubmit()</span>
|
||||
<span class="cstat-no" title="statement not covered" > } else if (e.key === 'Escape') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onClose()</span>
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter' && (e.metaKey || <span class="branch-2 cbranch-no" title="branch not covered" >e.ctrlKey)) {</span>
|
||||
e.preventDefault()
|
||||
handleSubmit()
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>} else if (e.key === 'Escape') {
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={<span class="fstat-no" title="function not covered" >(i</span>sOpen) => { <span class="cstat-no" title="statement not covered" >if (!isOpen) <span class="cstat-no" title="statement not covered" >onClose() }</span></span>}>
|
||||
<Dialog open={open} onOpenChange={(isOpen) => { <span class="missing-if-branch" title="else path not taken" >E</span>if (!isOpen) onClose() }}>
|
||||
<DialogContent showCloseButton={false} className="sm:max-w-[420px]">
|
||||
<DialogHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<DialogTitle>Commit & Push</DialogTitle>
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{modifiedCount} file{modifiedCount !== 1 ? 's' : <span class="branch-1 cbranch-no" title="branch not covered" >''} </span>changed
|
||||
{modifiedCount} file{modifiedCount !== 1 ? 's' : ''} changed
|
||||
</Badge>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
@@ -263,7 +263,7 @@ export function CommitDialog({ open, modifiedCount, onCommit, onClose }: CommitD
|
||||
className="w-full resize-y rounded-lg border border-input bg-[var(--bg-input)] px-3 py-2.5 text-[13px] text-foreground placeholder:text-muted-foreground outline-none transition-colors focus:border-ring"
|
||||
placeholder="Commit message..."
|
||||
value={message}
|
||||
onChange={<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >setMessage(e.target.value)}</span>
|
||||
onChange={(e) => setMessage(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
rows={3}
|
||||
/>
|
||||
@@ -289,7 +289,7 @@ export function CommitDialog({ open, modifiedCount, onCommit, onClose }: CommitD
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">36% </span>
|
||||
<span class="strong">92% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>9/25</span>
|
||||
<span class='fraction'>23/25</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">35.29% </span>
|
||||
<span class="strong">88.23% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>6/17</span>
|
||||
<span class='fraction'>15/17</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">30% </span>
|
||||
<span class="strong">90% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>3/10</span>
|
||||
<span class='fraction'>9/10</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">40.9% </span>
|
||||
<span class="strong">95.23% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>9/22</span>
|
||||
<span class='fraction'>20/21</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -211,27 +211,27 @@
|
||||
<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">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">25x</span>
|
||||
<span class="cline-any cline-yes">25x</span>
|
||||
<span class="cline-any cline-yes">25x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">7x</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-yes">25x</span>
|
||||
<span class="cline-any cline-yes">11x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">10x</span>
|
||||
<span class="cline-any cline-yes">10x</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-yes">22x</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-yes">25x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">25x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -246,7 +246,7 @@
|
||||
<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-yes">6x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -255,7 +255,7 @@
|
||||
<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">176x</span>
|
||||
<span class="cline-any cline-yes">200x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -264,13 +264,13 @@
|
||||
<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-yes">1x</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-yes">6x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -281,7 +281,7 @@
|
||||
<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-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -335,19 +335,19 @@ export function CreateNoteDialog({ open, onClose, onCreate, defaultType, customT
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (open) {
|
||||
<span class="cstat-no" title="statement not covered" > setTitle('')</span>
|
||||
<span class="cstat-no" title="statement not covered" > setType(defaultType ?? 'Note')</span>
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >i</span>nputRef.current?.focus(), 5</span>0)</span>
|
||||
if (open) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- reset on dialog open
|
||||
setTitle(''); setType(defaultType ?? 'Note')
|
||||
setTimeout(() => inputRef.current?.focus(), 50)
|
||||
}
|
||||
}, [open, defaultType])
|
||||
|
||||
const handleSubmit = <span class="fstat-no" title="function not covered" >(e</span>: React.FormEvent) => {
|
||||
<span class="cstat-no" title="statement not covered" > e.preventDefault()</span>
|
||||
const trimmed = <span class="cstat-no" title="statement not covered" >title.trim()</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (!trimmed) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > onCreate(trimmed, type)</span>
|
||||
<span class="cstat-no" title="statement not covered" > onClose()</span>
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
const trimmed = title.trim()
|
||||
if (!trimmed) return
|
||||
onCreate(trimmed, type)
|
||||
onClose()
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -365,7 +365,7 @@ export function CreateNoteDialog({ open, onClose, onCreate, defaultType, customT
|
||||
ref={inputRef}
|
||||
placeholder="Enter note title..."
|
||||
value={title}
|
||||
onChange={<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >setTitle(e.target.value)}</span>
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-1.5">
|
||||
@@ -383,13 +383,13 @@ export function CreateNoteDialog({ open, onClose, onCreate, defaultType, customT
|
||||
"rounded-full text-xs",
|
||||
type === t && "bg-primary text-primary-foreground"
|
||||
)}
|
||||
onClick={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etType(t)}</span>
|
||||
onClick={() => setType(t)}
|
||||
>
|
||||
{t}
|
||||
</Button>
|
||||
))}
|
||||
{customTypes.map(<span class="fstat-no" title="function not covered" >(t</span>) => (
|
||||
<span class="cstat-no" title="statement not covered" > <Button</span>
|
||||
{customTypes.map((t) => (
|
||||
<Button
|
||||
key={t}
|
||||
type="button"
|
||||
variant={type === t ? 'default' : 'outline'}
|
||||
@@ -400,7 +400,7 @@ export function CreateNoteDialog({ open, onClose, onCreate, defaultType, customT
|
||||
? "bg-[var(--accent-blue)] text-white"
|
||||
: "border-[var(--accent-blue)] text-[var(--accent-blue)]"
|
||||
)}
|
||||
onClick={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etType(t)}</span>
|
||||
onClick={() => setType(t)}
|
||||
>
|
||||
{t}
|
||||
</Button>
|
||||
@@ -427,7 +427,7 @@ export function CreateNoteDialog({ open, onClose, onCreate, defaultType, customT
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -139,17 +139,17 @@
|
||||
<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">38x</span>
|
||||
<span class="cline-any cline-yes">38x</span>
|
||||
<span class="cline-any cline-yes">51x</span>
|
||||
<span class="cline-any cline-yes">51x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">38x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-yes">51x</span>
|
||||
<span class="cline-any cline-yes">18x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</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-yes">38x</span>
|
||||
<span class="cline-any cline-yes">51x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
@@ -157,7 +157,7 @@
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">38x</span>
|
||||
<span class="cline-any cline-yes">51x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -208,7 +208,7 @@ export function CreateTypeDialog({ open, onClose, onCreate }: CreateTypeDialogPr
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setName('')
|
||||
setName('') // eslint-disable-line react-hooks/set-state-in-effect -- reset on dialog open
|
||||
setTimeout(() => inputRef.current?.focus(), 50)
|
||||
}
|
||||
}, [open])
|
||||
@@ -262,7 +262,7 @@ export function CreateTypeDialog({ open, onClose, onCreate }: CreateTypeDialogPr
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/14</span>
|
||||
<span class='fraction'>16/16</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="strong">93.75% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/21</span>
|
||||
<span class='fraction'>15/16</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/2</span>
|
||||
<span class='fraction'>5/5</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/14</span>
|
||||
<span class='fraction'>11/11</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -105,41 +105,24 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<a name='L43'></a><a href='#L43'>43</a></td><td class="line-coverage quiet"><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-yes">3x</span>
|
||||
<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-neutral"> </span>
|
||||
<span class="cline-any cline-yes">18x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-yes">43x</span>
|
||||
<span class="cline-any cline-yes">4x</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-no"> </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-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-no"> </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-no"> </span>
|
||||
<span class="cline-any cline-yes">18x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -148,53 +131,61 @@
|
||||
<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">8x</span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">18x</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></td><td class="text"><pre class="prettyprint lang-js">import { cn } from '@/lib/utils'
|
||||
|
||||
interface DiffViewProps {
|
||||
diff: string
|
||||
}
|
||||
|
||||
export function <span class="fstat-no" title="function not covered" >DiffView({</span> diff }: DiffViewProps) {
|
||||
<span class="cstat-no" title="statement not covered" > if (!diff) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
const DIFF_HEADER_PREFIXES = ['diff', 'index', '---', '+++', 'new file']
|
||||
|
||||
function classifyDiffLine(line: string): string {
|
||||
if (line.startsWith('+') && !line.startsWith('+++')) return 'bg-[rgba(76,175,80,0.12)] text-[#4caf50]'
|
||||
if (line.startsWith('-') && !line.startsWith('---')) return 'bg-[rgba(244,67,54,0.12)] text-[#f44336]'
|
||||
if (line.startsWith('@@')) return 'bg-[rgba(33,150,243,0.08)] text-primary italic'
|
||||
if (DIFF_HEADER_PREFIXES.some((p) => line.startsWith(p))) return 'bg-muted text-muted-foreground font-semibold'
|
||||
return 'text-secondary-foreground'
|
||||
}
|
||||
|
||||
function DiffLine({ line, lineNumber }: { line: string; lineNumber: number }) {
|
||||
return (
|
||||
<div className={cn("flex min-h-[22px] px-4", classifyDiffLine(line))}>
|
||||
<span className="w-10 shrink-0 text-right pr-3 text-muted-foreground select-none">{lineNumber}</span>
|
||||
<span className="flex-1 whitespace-pre-wrap break-all px-2">{line || <span class="branch-1 cbranch-no" title="branch not covered" >'\u00A0'}<</span>/span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export function DiffView({ diff }: DiffViewProps) {
|
||||
if (!diff) {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center text-sm text-muted-foreground">
|
||||
No changes to display
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const lines = <span class="cstat-no" title="statement not covered" >diff.split('\n')</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
return (
|
||||
<div className="font-mono text-[13px] leading-relaxed py-3">
|
||||
{lines.map(<span class="fstat-no" title="function not covered" >(l</span>ine, i) => {
|
||||
let lineClass = <span class="cstat-no" title="statement not covered" >'text-secondary-foreground'</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (line.startsWith('+') && !line.startsWith('+++')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > lineClass = 'bg-[rgba(76,175,80,0.12)] text-[#4caf50]'</span>
|
||||
<span class="cstat-no" title="statement not covered" > } else if (line.startsWith('-') && !line.startsWith('---')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > lineClass = 'bg-[rgba(244,67,54,0.12)] text-[#f44336]'</span>
|
||||
<span class="cstat-no" title="statement not covered" > } else if (line.startsWith('@@')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > lineClass = 'bg-[rgba(33,150,243,0.08)] text-primary italic'</span>
|
||||
<span class="cstat-no" title="statement not covered" > } else if (line.startsWith('diff') || line.startsWith('index') || line.startsWith('---') || line.startsWith('+++') || line.startsWith('new file')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > lineClass = 'bg-muted text-muted-foreground font-semibold'</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<div key={i} className={cn("flex min-h-[22px] px-4", lineClass)}>
|
||||
<span className="w-10 shrink-0 text-right pr-3 text-muted-foreground select-none">
|
||||
{i + 1}
|
||||
</span>
|
||||
<span className="flex-1 whitespace-pre-wrap break-all px-2">
|
||||
{line || '\u00A0'}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
{diff.split('\n').map((line, i) => (
|
||||
<DiffLine key={i} line={line} lineNumber={i + 1} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -205,7 +196,7 @@ export function <span class="fstat-no" title="function not covered" >DiffView({<
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">24.59% </span>
|
||||
<span class="strong">97.72% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>15/61</span>
|
||||
<span class='fraction'>86/88</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">16.66% </span>
|
||||
<span class="strong">82.5% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>5/30</span>
|
||||
<span class='fraction'>33/40</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">15% </span>
|
||||
<span class="strong">92.85% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>3/20</span>
|
||||
<span class='fraction'>26/28</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">26.31% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>15/57</span>
|
||||
<span class='fraction'>82/82</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -244,7 +244,91 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<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></td><td class="line-coverage quiet"><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>
|
||||
@@ -259,43 +343,56 @@
|
||||
<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">46x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">46x</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-yes">17x</span>
|
||||
<span class="cline-any cline-yes">17x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">17x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">46x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">17x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</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-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">4x</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-yes">17x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">46x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">17x</span>
|
||||
<span class="cline-any cline-yes">7x</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-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">10x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -304,92 +401,64 @@
|
||||
<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">14x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">14x</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>
|
||||
<span class="cline-any cline-yes">14x</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-no"> </span>
|
||||
<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-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">14x</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>
|
||||
<span class="cline-any cline-yes">14x</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>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">14x</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-no"> </span>
|
||||
<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-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-yes">14x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">42x</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-no"> </span>
|
||||
<span class="cline-any cline-yes">45x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">45x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">45x</span>
|
||||
<span class="cline-any cline-yes">12x</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-yes">5x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">33x</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-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -403,29 +472,212 @@
|
||||
<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">52x</span>
|
||||
<span class="cline-any cline-yes">52x</span>
|
||||
<span class="cline-any cline-yes">52x</span>
|
||||
<span class="cline-any cline-yes">52x</span>
|
||||
<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-yes">52x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<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-neutral"> </span>
|
||||
<span class="cline-any cline-yes">52x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</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-yes">52x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">52x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</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-yes">52x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</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></td><td class="text"><pre class="prettyprint lang-js">import { useState } from 'react'
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">52x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">93x</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-yes">2x</span>
|
||||
<span class="cline-any cline-yes">3x</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-yes">4x</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-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">5x</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></td><td class="text"><pre class="prettyprint lang-js">import { useState, useCallback, useRef } from 'react'
|
||||
import { normalizeUrl, openExternalUrl } from '../utils/url'
|
||||
|
||||
export function UrlValue({
|
||||
value,
|
||||
onSave,
|
||||
onCancel,
|
||||
isEditing,
|
||||
onStartEdit,
|
||||
}: {
|
||||
value: string
|
||||
onSave: (newValue: string) => void
|
||||
onCancel: () => void
|
||||
isEditing: boolean
|
||||
onStartEdit: () => void
|
||||
}) {
|
||||
const [editValue, setEditValue] = useState(value)
|
||||
const openingRef = useRef(false)
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
onSave(editValue)
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>} else if (e.key === 'Escape') {
|
||||
setEditValue(value)
|
||||
onCancel()
|
||||
}
|
||||
}
|
||||
|
||||
const handleOpen = useCallback((e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (openingRef.current) <span class="cstat-no" title="statement not covered" >return</span>
|
||||
openingRef.current = true
|
||||
const normalized = normalizeUrl(value)
|
||||
try {
|
||||
new URL(normalized)
|
||||
openExternalUrl(normalized).catch(<span class="fstat-no" title="function not covered" >() => {</span>
|
||||
// opener failed — ignore silently
|
||||
})
|
||||
} catch {
|
||||
// malformed URL — do nothing
|
||||
} finally {
|
||||
setTimeout(<span class="fstat-no" title="function not covered" >() => {</span> <span class="cstat-no" title="statement not covered" >openingRef.current = false }</span>, 500)
|
||||
}
|
||||
}, [value])
|
||||
|
||||
const handleEditClick = useCallback((e: React.MouseEvent) => {
|
||||
e.stopPropagation()
|
||||
onStartEdit()
|
||||
}, [onStartEdit])
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<input
|
||||
className="w-full rounded border border-ring bg-muted px-2 py-1 text-[12px] text-foreground outline-none focus:border-primary"
|
||||
type="text"
|
||||
value={editValue}
|
||||
onChange={(e) => setEditValue(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={() => onSave(editValue)}
|
||||
autoFocus
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="group/url inline-flex min-w-0 items-center gap-1">
|
||||
<span
|
||||
className="min-w-0 cursor-pointer truncate rounded px-1 py-0.5 text-right text-[12px] text-secondary-foreground transition-colors hover:text-primary hover:underline"
|
||||
onClick={handleOpen}
|
||||
title={value}
|
||||
data-testid="url-link"
|
||||
>
|
||||
{value || <span class="branch-1 cbranch-no" title="branch not covered" >'\u2014'}</span>
|
||||
</span>
|
||||
<button
|
||||
className="shrink-0 border-none bg-transparent p-0 text-[12px] leading-none text-muted-foreground opacity-0 transition-all hover:text-foreground group-hover/url:opacity-100"
|
||||
onClick={handleEditClick}
|
||||
title="Edit URL"
|
||||
data-testid="url-edit-btn"
|
||||
>
|
||||
&#9998;
|
||||
</button>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export function EditableValue({
|
||||
value,
|
||||
@@ -442,24 +694,24 @@ export function EditableValue({
|
||||
}) {
|
||||
const [editValue, setEditValue] = useState(value)
|
||||
|
||||
const handleKeyDown = <span class="fstat-no" title="function not covered" >(e</span>: React.KeyboardEvent) => {
|
||||
<span class="cstat-no" title="statement not covered" > if (e.key === 'Enter') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onSave(editValue)</span>
|
||||
<span class="cstat-no" title="statement not covered" > } else if (e.key === 'Escape') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > setEditValue(value)</span>
|
||||
<span class="cstat-no" title="statement not covered" > onCancel()</span>
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
onSave(editValue)
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>} else if (e.key === 'Escape') {
|
||||
setEditValue(value)
|
||||
onCancel()
|
||||
}
|
||||
}
|
||||
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (isEditing) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
if (isEditing) {
|
||||
return (
|
||||
<input
|
||||
className="w-full rounded border border-ring bg-muted px-2 py-1 text-[13px] text-foreground outline-none focus:border-primary"
|
||||
className="w-full rounded border border-ring bg-muted px-2 py-1 text-[12px] text-foreground outline-none focus:border-primary"
|
||||
type="text"
|
||||
value={editValue}
|
||||
onChange={<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >setEditValue(e.target.value)}</span>
|
||||
onChange={(e) => setEditValue(e.target.value)}
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >o</span>nSave(editValue)}</span>
|
||||
onBlur={() => onSave(editValue)}
|
||||
autoFocus
|
||||
/>
|
||||
)
|
||||
@@ -467,11 +719,11 @@ export function EditableValue({
|
||||
|
||||
return (
|
||||
<span
|
||||
className="min-w-0 cursor-pointer truncate rounded px-1 py-0.5 text-right text-secondary-foreground transition-colors hover:bg-muted"
|
||||
className="min-w-0 cursor-pointer truncate rounded px-1 py-0.5 text-right text-[12px] text-secondary-foreground transition-colors hover:bg-muted"
|
||||
onClick={onStartEdit}
|
||||
title={value || <span class="branch-1 cbranch-no" title="branch not covered" >'Click to edit'}</span>
|
||||
title={value || 'Click to edit'}
|
||||
>
|
||||
{value || <span class="branch-1 cbranch-no" title="branch not covered" >'\u2014'}</span>
|
||||
{value || '\u2014'}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -490,48 +742,48 @@ export function TagPillList({
|
||||
const [isAddingNew, setIsAddingNew] = useState(false)
|
||||
const [newValue, setNewValue] = useState('')
|
||||
|
||||
const handleStartEdit = <span class="fstat-no" title="function not covered" >(i</span>ndex: number) => {
|
||||
<span class="cstat-no" title="statement not covered" > setEditingIndex(index)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setEditValue(items[index])</span>
|
||||
const handleStartEdit = (index: number) => {
|
||||
setEditingIndex(index)
|
||||
setEditValue(items[index])
|
||||
}
|
||||
|
||||
const handleSaveEdit = <span class="fstat-no" title="function not covered" >() => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (editingIndex !== null) {</span>
|
||||
const newItems = <span class="cstat-no" title="statement not covered" >[...items]</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (editValue.trim()) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > newItems[editingIndex] = editValue.trim()</span>
|
||||
const handleSaveEdit = () => {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (editingIndex !== null) {
|
||||
const newItems = [...items]
|
||||
if (editValue.trim()) {
|
||||
newItems[editingIndex] = editValue.trim()
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > newItems.splice(editingIndex, 1)</span>
|
||||
newItems.splice(editingIndex, 1)
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > onSave(newItems)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setEditingIndex(null)</span>
|
||||
onSave(newItems)
|
||||
setEditingIndex(null)
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteItem = <span class="fstat-no" title="function not covered" >(i</span>ndex: number) => {
|
||||
const newItems = <span class="cstat-no" title="statement not covered" >items.filter(<span class="fstat-no" title="function not covered" >(_</span>, i) => <span class="cstat-no" title="statement not covered" >i !== index)</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > onSave(newItems)</span>
|
||||
const handleDeleteItem = (index: number) => {
|
||||
const newItems = items.filter((_, i) => i !== index)
|
||||
onSave(newItems)
|
||||
}
|
||||
|
||||
const handleAddNew = <span class="fstat-no" title="function not covered" >() => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (newValue.trim()) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onSave([...items, newValue.trim()])</span>
|
||||
<span class="cstat-no" title="statement not covered" > setNewValue('')</span>
|
||||
<span class="cstat-no" title="statement not covered" > setIsAddingNew(false)</span>
|
||||
const handleAddNew = () => {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (newValue.trim()) {
|
||||
onSave([...items, newValue.trim()])
|
||||
setNewValue('')
|
||||
setIsAddingNew(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleKeyDown = <span class="fstat-no" title="function not covered" >(e</span>: React.KeyboardEvent, action: 'edit' | 'add') => {
|
||||
<span class="cstat-no" title="statement not covered" > if (e.key === 'Enter') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (action === 'edit') <span class="cstat-no" title="statement not covered" >handleSaveEdit()</span></span>
|
||||
else <span class="cstat-no" title="statement not covered" >handleAddNew()</span>
|
||||
<span class="cstat-no" title="statement not covered" > } else if (e.key === 'Escape') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (action === 'edit') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > setEditingIndex(null)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setEditValue('')</span>
|
||||
const handleKeyDown = (e: React.KeyboardEvent, action: 'edit' | 'add') => {
|
||||
if (e.key === 'Enter') {
|
||||
if (action === 'edit') handleSaveEdit()
|
||||
else handleAddNew()
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>} else if (e.key === 'Escape') {
|
||||
if (action === 'edit') {
|
||||
setEditingIndex(null)
|
||||
setEditValue('')
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > setIsAddingNew(false)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setNewValue('')</span>
|
||||
setIsAddingNew(false)
|
||||
setNewValue('')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -540,14 +792,14 @@ export function TagPillList({
|
||||
<div className="flex flex-wrap items-center gap-1">
|
||||
{items.map((item, idx) =>
|
||||
editingIndex === idx ? (
|
||||
<span class="branch-0 cbranch-no" title="branch not covered" > <input</span>
|
||||
<input
|
||||
key={idx}
|
||||
className="rounded-full border border-ring bg-muted px-2 py-0.5 text-[11px] text-foreground outline-none focus:border-primary"
|
||||
style={{ width: Math.max(60, editValue.length * 7 + 16) }}
|
||||
type="text"
|
||||
value={editValue}
|
||||
onChange={<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >setEditValue(e.target.value)}</span>
|
||||
onKeyDown={<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >handleKeyDown(e, 'edit')}</span>
|
||||
onChange={(e) => setEditValue(e.target.value)}
|
||||
onKeyDown={(e) => handleKeyDown(e, 'edit')}
|
||||
onBlur={handleSaveEdit}
|
||||
autoFocus
|
||||
/>
|
||||
@@ -561,16 +813,16 @@ export function TagPillList({
|
||||
fontSize: 11,
|
||||
fontWeight: 500,
|
||||
}}
|
||||
onClick={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >h</span>andleStartEdit(idx)}</span>
|
||||
onClick={() => handleStartEdit(idx)}
|
||||
title="Click to edit"
|
||||
>
|
||||
{item}
|
||||
<button
|
||||
className="ml-0.5 inline-flex h-3.5 w-3.5 items-center justify-center rounded-full border-none bg-transparent p-0 text-[10px] leading-none opacity-0 transition-all hover:bg-[var(--accent-red-light)] hover:text-[var(--accent-red)] group-hover/pill:opacity-100"
|
||||
style={{ color: 'var(--accent-blue)' }}
|
||||
onClick={<span class="fstat-no" title="function not covered" >(e</span>) => {
|
||||
<span class="cstat-no" title="statement not covered" > e.stopPropagation()</span>
|
||||
<span class="cstat-no" title="statement not covered" > handleDeleteItem(idx)</span>
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleDeleteItem(idx)
|
||||
}}
|
||||
title="Remove"
|
||||
>
|
||||
@@ -580,16 +832,16 @@ export function TagPillList({
|
||||
)
|
||||
)}
|
||||
{isAddingNew ? (
|
||||
<span class="branch-0 cbranch-no" title="branch not covered" > <input</span>
|
||||
<input
|
||||
className="rounded-full border border-ring bg-muted px-2 py-0.5 text-[11px] text-foreground outline-none focus:border-primary"
|
||||
style={{ width: Math.max(60, newValue.length * 7 + 16) }}
|
||||
type="text"
|
||||
value={newValue}
|
||||
onChange={<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >setNewValue(e.target.value)}</span>
|
||||
onKeyDown={<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >handleKeyDown(e, 'add')}</span>
|
||||
onBlur={<span class="fstat-no" title="function not covered" >() => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (newValue.trim()) <span class="cstat-no" title="statement not covered" >handleAddNew()</span></span>
|
||||
else { <span class="cstat-no" title="statement not covered" >setIsAddingNew(false); <span class="cstat-no" title="statement not covered" >s</span>etNewValue('') }</span>
|
||||
onChange={(e) => setNewValue(e.target.value)}
|
||||
onKeyDown={(e) => handleKeyDown(e, 'add')}
|
||||
onBlur={() => {
|
||||
if (newValue.trim()) handleAddNew()
|
||||
else { setIsAddingNew(false); setNewValue('') }
|
||||
}}
|
||||
placeholder={`${label}...`}
|
||||
autoFocus
|
||||
@@ -597,7 +849,7 @@ export function TagPillList({
|
||||
) : (
|
||||
<button
|
||||
className="inline-flex h-5 w-5 items-center justify-center rounded-full border border-dashed border-[var(--accent-blue)] bg-transparent p-0 text-[12px] leading-none text-[var(--accent-blue)] transition-colors hover:bg-[var(--accent-blue-light)]"
|
||||
onClick={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etIsAddingNew(true)}</span>
|
||||
onClick={() => setIsAddingNew(true)}
|
||||
title={`Add ${label.toLowerCase()}`}
|
||||
>
|
||||
+
|
||||
@@ -613,7 +865,7 @@ export function TagPillList({
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">27.02% </span>
|
||||
<span class="strong">97.14% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>20/74</span>
|
||||
<span class='fraction'>34/35</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">17.07% </span>
|
||||
<span class="strong">91.66% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>7/41</span>
|
||||
<span class='fraction'>11/12</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">31.81% </span>
|
||||
<span class="strong">90.9% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>7/22</span>
|
||||
<span class='fraction'>10/11</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">25.39% </span>
|
||||
<span class="strong">96.77% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>16/63</span>
|
||||
<span class='fraction'>30/31</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -144,71 +144,7 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<a name='L82'></a><a href='#L82'>82</a></td><td class="line-coverage quiet"><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>
|
||||
@@ -221,102 +157,65 @@
|
||||
<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">57x</span>
|
||||
<span class="cline-any cline-yes">57x</span>
|
||||
<span class="cline-any cline-yes">57x</span>
|
||||
<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-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-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-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-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">57x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">7x</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-yes">11x</span>
|
||||
<span class="cline-any cline-yes">11x</span>
|
||||
<span class="cline-any cline-yes">11x</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-yes">22x</span>
|
||||
<span class="cline-any cline-yes">21x</span>
|
||||
<span class="cline-any cline-yes">57x</span>
|
||||
<span class="cline-any cline-yes">47x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</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-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">57x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">21x</span>
|
||||
<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-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-yes">22x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">7x</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>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</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-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-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>
|
||||
<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-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">2x</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-yes">43x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -326,37 +225,10 @@
|
||||
<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-no"> </span>
|
||||
<span class="cline-any cline-no"> </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>
|
||||
<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 { useState, useRef, useEffect, useMemo } from 'react'
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { useState, useRef, useEffect } from 'react'
|
||||
import type { VaultEntry } from '../types'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { NoteSearchList } from './NoteSearchList'
|
||||
import { useNoteSearch } from '../hooks/useNoteSearch'
|
||||
|
||||
interface QuickOpenPaletteProps {
|
||||
open: boolean
|
||||
@@ -365,97 +237,50 @@ interface QuickOpenPaletteProps {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
/** Simple fuzzy match: all query chars appear in order in the target */
|
||||
function <span class="fstat-no" title="function not covered" >fuzzyMatch(q</span>uery: string, target: string): { match: boolean; score: number } {
|
||||
const q = <span class="cstat-no" title="statement not covered" >query.toLowerCase()</span>
|
||||
const t = <span class="cstat-no" title="statement not covered" >target.toLowerCase()</span>
|
||||
let qi = <span class="cstat-no" title="statement not covered" >0</span>
|
||||
let score = <span class="cstat-no" title="statement not covered" >0</span>
|
||||
let lastMatchIndex = <span class="cstat-no" title="statement not covered" >-1</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > for (let ti = <span class="cstat-no" title="statement not covered" >0; t</span>i < t.length && qi < q.length; ti++) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (t[ti] === q[qi]) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (ti === lastMatchIndex + 1) <span class="cstat-no" title="statement not covered" >score += 2</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > if (ti === 0 || t[ti - 1] === ' ' || t[ti - 1] === '-') <span class="cstat-no" title="statement not covered" >score += 3</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > score += 1</span>
|
||||
<span class="cstat-no" title="statement not covered" > lastMatchIndex = ti</span>
|
||||
<span class="cstat-no" title="statement not covered" > qi++</span>
|
||||
}
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return { match: qi === q.length, score }</span>
|
||||
}
|
||||
|
||||
export function QuickOpenPalette({ open, entries, onSelect, onClose }: QuickOpenPaletteProps) {
|
||||
const [query, setQuery] = useState('')
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const listRef = useRef<HTMLDivElement>(null)
|
||||
const { results, selectedIndex, setSelectedIndex, handleKeyDown } = useNoteSearch(entries, query)
|
||||
|
||||
useEffect(() => {
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (open) {
|
||||
<span class="cstat-no" title="statement not covered" > setQuery('')</span>
|
||||
<span class="cstat-no" title="statement not covered" > setSelectedIndex(0)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >i</span>nputRef.current?.focus(), 5</span>0)</span>
|
||||
if (open) {
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- reset on dialog open
|
||||
setQuery('')
|
||||
setSelectedIndex(0)
|
||||
setTimeout(() => inputRef.current?.focus(), 50)
|
||||
}
|
||||
}, [open])
|
||||
|
||||
const results = useMemo(() => {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (!query.trim()) {
|
||||
return [...entries].sort((a, b) => (b.modifiedAt ?? <span class="branch-1 cbranch-no" title="branch not covered" >0) - (a</span>.modifiedAt ?? <span class="branch-1 cbranch-no" title="branch not covered" >0))</span>.slice(0, 20)
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > return entries</span>
|
||||
.map(<span class="fstat-no" title="function not covered" >(e</span>ntry) => (<span class="cstat-no" title="statement not covered" >{ entry, ...fuzzyMatch(query, entry.title) }))</span>
|
||||
.filter(<span class="fstat-no" title="function not covered" >(r</span>) => <span class="cstat-no" title="statement not covered" >r.match)</span>
|
||||
.sort(<span class="fstat-no" title="function not covered" >(a</span>, b) => <span class="cstat-no" title="statement not covered" >b.score - a.score)</span>
|
||||
.slice(0, 20)
|
||||
.map(<span class="fstat-no" title="function not covered" >(r</span>) => <span class="cstat-no" title="statement not covered" >r.entry)</span>
|
||||
}, [entries, query])
|
||||
}, [open, setSelectedIndex])
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedIndex(0)
|
||||
}, [query])
|
||||
|
||||
useEffect(() => {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (!listRef.current) return
|
||||
const selected = <span class="cstat-no" title="statement not covered" >listRef.current.children[selectedIndex] as HTMLElement | undefined</span>
|
||||
<span class="cstat-no" title="statement not covered" > selected?.scrollIntoView({ block: 'nearest' })</span>
|
||||
}, [selectedIndex])
|
||||
|
||||
useEffect(() => {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (!open) return
|
||||
const handleKey = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >(e</span>: KeyboardEvent) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (e.key === 'Escape') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > e.preventDefault()</span>
|
||||
<span class="cstat-no" title="statement not covered" > onClose()</span>
|
||||
<span class="cstat-no" title="statement not covered" > } else if (e.key === 'ArrowDown') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > e.preventDefault()</span>
|
||||
<span class="cstat-no" title="statement not covered" > setSelectedIndex(<span class="fstat-no" title="function not covered" >(i</span>) => <span class="cstat-no" title="statement not covered" >Math.min(i + 1, results.length - 1))</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > } else if (e.key === 'ArrowUp') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > e.preventDefault()</span>
|
||||
<span class="cstat-no" title="statement not covered" > setSelectedIndex(<span class="fstat-no" title="function not covered" >(i</span>) => <span class="cstat-no" title="statement not covered" >Math.max(i - 1, 0))</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > } else if (e.key === 'Enter') {</span>
|
||||
<span class="cstat-no" title="statement not covered" > e.preventDefault()</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (results[selectedIndex]) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onSelect(results[selectedIndex])</span>
|
||||
<span class="cstat-no" title="statement not covered" > onClose()</span>
|
||||
if (!open) return
|
||||
const handler = (e: KeyboardEvent) => {
|
||||
handleKeyDown(e)
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault()
|
||||
onClose()
|
||||
} else if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
const selected = results[selectedIndex]
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (selected) {
|
||||
onSelect(selected.entry)
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > window.addEventListener('keydown', handleKey)</span>
|
||||
<span class="cstat-no" title="statement not covered" > return <span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >w</span>indow.removeEventListener('keydown', handleKey)</span></span>
|
||||
}, [open, results, selectedIndex, onSelect, onClose])
|
||||
window.addEventListener('keydown', handler)
|
||||
return () => window.removeEventListener('keydown', handler)
|
||||
}, [open, results, selectedIndex, onSelect, onClose, handleKeyDown])
|
||||
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (!open) return null
|
||||
if (!open) return null
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-[1000] flex justify-center bg-[var(--shadow-dialog)] pt-[15vh]"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
className="flex w-[500px] max-w-[90vw] max-h-[400px] flex-col self-start overflow-hidden rounded-xl border border-[var(--border-dialog)] bg-popover shadow-[0_8px_32px_var(--shadow-dialog)]"
|
||||
onClick={<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >e.stopPropagation()}</span>
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<input
|
||||
ref={inputRef}
|
||||
@@ -463,37 +288,20 @@ export function QuickOpenPalette({ open, entries, onSelect, onClose }: QuickOpen
|
||||
type="text"
|
||||
placeholder="Search notes..."
|
||||
value={query}
|
||||
onChange={<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >setQuery(e.target.value)}</span>
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
/>
|
||||
<NoteSearchList
|
||||
items={results}
|
||||
selectedIndex={selectedIndex}
|
||||
getItemKey={(item) => item.entry.path}
|
||||
onItemClick={(item) => {
|
||||
onSelect(item.entry)
|
||||
onClose()
|
||||
}}
|
||||
onItemHover={<span class="fstat-no" title="function not covered" >(i</span>) => <span class="cstat-no" title="statement not covered" >setSelectedIndex(i)}</span>
|
||||
emptyMessage="No matching notes"
|
||||
className="flex-1 overflow-y-auto"
|
||||
/>
|
||||
<div className="flex-1 overflow-y-auto py-1" ref={listRef}>
|
||||
{results.length === 0 ? (
|
||||
<div className="px-4 py-4 text-center text-[13px] text-muted-foreground">
|
||||
No matching notes
|
||||
</div>
|
||||
) : (
|
||||
results.map(<span class="fstat-no" title="function not covered" >(e</span>ntry, i) => (
|
||||
<span class="cstat-no" title="statement not covered" > <div</span>
|
||||
key={entry.path}
|
||||
className={cn(
|
||||
"flex cursor-pointer items-center justify-between px-4 py-2 transition-colors",
|
||||
i === selectedIndex ? "bg-accent" : "hover:bg-secondary"
|
||||
)}
|
||||
onClick={<span class="fstat-no" title="function not covered" >() => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onSelect(entry)</span>
|
||||
<span class="cstat-no" title="statement not covered" > onClose()</span>
|
||||
}}
|
||||
onMouseEnter={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etSelectedIndex(i)}</span>
|
||||
>
|
||||
<span className="text-sm text-foreground">{entry.title}</span>
|
||||
{entry.isA && (
|
||||
<Badge variant="secondary" className="text-[11px]">
|
||||
{entry.isA}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -505,7 +313,7 @@ export function QuickOpenPalette({ open, entries, onSelect, onClose }: QuickOpen
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">35.71% </span>
|
||||
<span class="strong">97.61% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>15/42</span>
|
||||
<span class='fraction'>41/42</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">7.14% </span>
|
||||
<span class="strong">78.57% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>1/14</span>
|
||||
<span class='fraction'>11/14</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">42.85% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>3/7</span>
|
||||
<span class='fraction'>7/7</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">37.5% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>15/40</span>
|
||||
<span class='fraction'>40/40</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -144,67 +144,67 @@
|
||||
<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">67x</span>
|
||||
<span class="cline-any cline-yes">67x</span>
|
||||
<span class="cline-any cline-yes">67x</span>
|
||||
<span class="cline-any cline-yes">67x</span>
|
||||
<span class="cline-any cline-yes">107x</span>
|
||||
<span class="cline-any cline-yes">107x</span>
|
||||
<span class="cline-any cline-yes">107x</span>
|
||||
<span class="cline-any cline-yes">107x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">67x</span>
|
||||
<span class="cline-any cline-yes">107x</span>
|
||||
<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-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</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-yes">67x</span>
|
||||
<span class="cline-any cline-yes">22x</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-yes">107x</span>
|
||||
<span class="cline-any cline-yes">39x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<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-yes">4x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">2x</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-yes">22x</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-yes">39x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<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-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<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-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">39x</span>
|
||||
<span class="cline-any cline-yes">39x</span>
|
||||
<span class="cline-any cline-yes">39x</span>
|
||||
<span class="cline-any cline-yes">39x</span>
|
||||
<span class="cline-any cline-yes">39x</span>
|
||||
<span class="cline-any cline-yes">39x</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-yes">67x</span>
|
||||
<span class="cline-any cline-yes">107x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -224,47 +224,47 @@ export function ResizeHandle({ onResize }: ResizeHandleProps) {
|
||||
const rafId = useRef(0)
|
||||
|
||||
const handleMouseDown = useCallback(
|
||||
<span class="fstat-no" title="function not covered" > (e</span>: React.MouseEvent) => {
|
||||
<span class="cstat-no" title="statement not covered" > e.preventDefault()</span>
|
||||
<span class="cstat-no" title="statement not covered" > isDragging.current = true</span>
|
||||
<span class="cstat-no" title="statement not covered" > lastX.current = e.clientX</span>
|
||||
<span class="cstat-no" title="statement not covered" > pendingDelta.current = 0</span>
|
||||
<span class="cstat-no" title="statement not covered" > document.body.style.cursor = 'col-resize'</span>
|
||||
<span class="cstat-no" title="statement not covered" > document.body.style.userSelect = 'none'</span>
|
||||
(e: React.MouseEvent) => {
|
||||
e.preventDefault()
|
||||
isDragging.current = true
|
||||
lastX.current = e.clientX
|
||||
pendingDelta.current = 0
|
||||
document.body.style.cursor = 'col-resize'
|
||||
document.body.style.userSelect = 'none'
|
||||
},
|
||||
[],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const handleMouseMove = <span class="fstat-no" title="function not covered" >(e</span>: MouseEvent) => {
|
||||
<span class="cstat-no" title="statement not covered" > if (!isDragging.current) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > pendingDelta.current += e.clientX - lastX.current</span>
|
||||
<span class="cstat-no" title="statement not covered" > lastX.current = e.clientX</span>
|
||||
const handleMouseMove = (e: MouseEvent) => {
|
||||
if (!isDragging.current) return
|
||||
pendingDelta.current += e.clientX - lastX.current
|
||||
lastX.current = e.clientX
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > if (!rafId.current) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > rafId.current = requestAnimationFrame(<span class="fstat-no" title="function not covered" >() => {</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > if (pendingDelta.current !== 0) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onResize(pendingDelta.current)</span>
|
||||
<span class="cstat-no" title="statement not covered" > pendingDelta.current = 0</span>
|
||||
if (!rafId.current) {
|
||||
rafId.current = requestAnimationFrame(() => {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (pendingDelta.current !== 0) {
|
||||
onResize(pendingDelta.current)
|
||||
pendingDelta.current = 0
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > rafId.current = 0</span>
|
||||
rafId.current = 0
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleMouseUp = <span class="fstat-no" title="function not covered" >() => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (isDragging.current) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > isDragging.current = false</span>
|
||||
<span class="cstat-no" title="statement not covered" > document.body.style.cursor = ''</span>
|
||||
<span class="cstat-no" title="statement not covered" > document.body.style.userSelect = ''</span>
|
||||
const handleMouseUp = () => {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (isDragging.current) {
|
||||
isDragging.current = false
|
||||
document.body.style.cursor = ''
|
||||
document.body.style.userSelect = ''
|
||||
// Flush any pending delta
|
||||
<span class="cstat-no" title="statement not covered" > if (rafId.current) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > cancelAnimationFrame(rafId.current)</span>
|
||||
<span class="cstat-no" title="statement not covered" > rafId.current = 0</span>
|
||||
if (rafId.current) {
|
||||
cancelAnimationFrame(rafId.current)
|
||||
rafId.current = 0
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > if (pendingDelta.current !== 0) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onResize(pendingDelta.current)</span>
|
||||
<span class="cstat-no" title="statement not covered" > pendingDelta.current = 0</span>
|
||||
if (pendingDelta.current !== 0) {
|
||||
onResize(pendingDelta.current)
|
||||
pendingDelta.current = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,7 +292,7 @@ export function ResizeHandle({ onResize }: ResizeHandleProps) {
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">34.78% </span>
|
||||
<span class="strong">65% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>8/23</span>
|
||||
<span class='fraction'>26/40</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">18.18% </span>
|
||||
<span class="strong">80.64% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>4/22</span>
|
||||
<span class='fraction'>25/31</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">27.27% </span>
|
||||
<span class="strong">52.17% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>3/11</span>
|
||||
<span class='fraction'>12/23</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">35.29% </span>
|
||||
<span class="strong">62.06% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>6/17</span>
|
||||
<span class='fraction'>18/29</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line medium'></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>
|
||||
@@ -214,15 +214,7 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<a name='L152'></a><a href='#L152'>152</a></td><td class="line-coverage quiet"><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>
|
||||
@@ -238,29 +230,103 @@
|
||||
<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">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">7x</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>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">16x</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-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</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-yes">65x</span>
|
||||
<span class="cline-any cline-yes">65x</span>
|
||||
<span class="cline-any cline-yes">66x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">65x</span>
|
||||
<span class="cline-any cline-yes">40x</span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">65x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">9x</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-yes">16x</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-yes">2x</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-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-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-no"> </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-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">52x</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>
|
||||
@@ -275,59 +341,6 @@
|
||||
<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>
|
||||
<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-no"> </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>
|
||||
<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-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -343,37 +356,8 @@
|
||||
<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-no"> </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>
|
||||
@@ -382,7 +366,7 @@
|
||||
<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 { useState, useRef, useEffect } from 'react'
|
||||
import { Package, GitBranch, RefreshCw, Sparkles, FileText, Bell, Settings, FolderOpen, Check } from 'lucide-react'
|
||||
import { Package, GitBranch, RefreshCw, Sparkles, FileText, Bell, Settings, FolderOpen, Check, Github, CircleDot } from 'lucide-react'
|
||||
|
||||
export interface VaultOption {
|
||||
label: string
|
||||
@@ -391,148 +375,140 @@ export interface VaultOption {
|
||||
|
||||
interface StatusBarProps {
|
||||
noteCount: number
|
||||
modifiedCount?: number
|
||||
vaultPath: string
|
||||
vaults: VaultOption[]
|
||||
onSwitchVault: (path: string) => void
|
||||
onOpenSettings?: () => void
|
||||
onOpenLocalFolder?: () => void
|
||||
onConnectGitHub?: () => void
|
||||
onClickPending?: () => void
|
||||
hasGitHub?: boolean
|
||||
}
|
||||
|
||||
export function StatusBar({ noteCount, vaultPath, vaults, onSwitchVault }: StatusBarProps) {
|
||||
const [showVaultMenu, setShowVaultMenu] = useState(false)
|
||||
const menuRef = useRef<HTMLDivElement>(null)
|
||||
function VaultMenuItem({ vault, isActive, onSelect }: { vault: VaultOption; isActive: boolean; onSelect: () => void }) {
|
||||
return (
|
||||
<div
|
||||
role="button" onClick={onSelect}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6, padding: '4px 8px', borderRadius: 4, cursor: 'pointer',
|
||||
background: isActive ? 'var(--hover)' : 'transparent',
|
||||
color: isActive ? 'var(--foreground)' : 'var(--muted-foreground)', fontSize: 12,
|
||||
}}
|
||||
onMouseEnter={<span class="fstat-no" title="function not covered" >(e</span>) => { <span class="cstat-no" title="statement not covered" >if (!isActive) <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'var(--hover)' }</span></span>}
|
||||
onMouseLeave={<span class="fstat-no" title="function not covered" >(e</span>) => { <span class="cstat-no" title="statement not covered" >if (!isActive) <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'transparent' }</span></span>}
|
||||
>
|
||||
{isActive ? <Check size={12} /> : <span style={{ width: 12 }} />}
|
||||
{vault.label}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function VaultMenu({ vaults, vaultPath, onSwitchVault, onOpenLocalFolder, onConnectGitHub, hasGitHub }: { vaults: VaultOption[]; vaultPath: string; onSwitchVault: (path: string) => void; onOpenLocalFolder?: () => void; onConnectGitHub?: () => void; hasGitHub?: boolean }) {
|
||||
const [open, setOpen] = useState(false)
|
||||
const menuRef = useRef<HTMLDivElement>(null)
|
||||
const activeVault = vaults.find((v) => v.path === vaultPath)
|
||||
|
||||
useEffect(() => {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (!showVaultMenu) return
|
||||
const handleClick = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >(e</span>: MouseEvent) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (menuRef.current && !menuRef.current.contains(e.target as Node)) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > setShowVaultMenu(false)</span>
|
||||
}
|
||||
if (!open) return
|
||||
const handleClick = (e: MouseEvent) => {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (menuRef.current && !menuRef.current.contains(e.target as Node)) setOpen(false)
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > document.addEventListener('mousedown', handleClick)</span>
|
||||
<span class="cstat-no" title="statement not covered" > return <span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >d</span>ocument.removeEventListener('mousedown', handleClick)</span></span>
|
||||
}, [showVaultMenu])
|
||||
document.addEventListener('mousedown', handleClick)
|
||||
return () => document.removeEventListener('mousedown', handleClick)
|
||||
}, [open])
|
||||
|
||||
return (
|
||||
<footer
|
||||
style={{
|
||||
height: 30,
|
||||
flexShrink: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
background: 'var(--sidebar)',
|
||||
borderTop: '1px solid var(--border)',
|
||||
padding: '0 8px',
|
||||
fontSize: 11,
|
||||
color: 'var(--muted-foreground)',
|
||||
}}
|
||||
>
|
||||
{/* Left section */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<div ref={menuRef} style={{ position: 'relative' }}>
|
||||
<span
|
||||
role="button"
|
||||
onClick={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >s</span>etShowVaultMenu(<span class="fstat-no" title="function not covered" >(v</span>) => <span class="cstat-no" title="statement not covered" >!v)</span>}</span>
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 4,
|
||||
cursor: 'pointer',
|
||||
padding: '2px 4px',
|
||||
borderRadius: 3,
|
||||
background: showVaultMenu ? <span class="branch-0 cbranch-no" title="branch not covered" >'var(--hover)' : '</span>transparent',
|
||||
}}
|
||||
title="Switch vault"
|
||||
>
|
||||
<FolderOpen size={13} />
|
||||
{activeVault?.label ?? <span class="branch-1 cbranch-no" title="branch not covered" >'Vault'}</span>
|
||||
</span>
|
||||
{showVaultMenu && (
|
||||
<span class="branch-1 cbranch-no" title="branch not covered" > <div</span>
|
||||
<div ref={menuRef} style={{ position: 'relative' }}>
|
||||
<span role="button" onClick={() => setOpen((v) => !v)} style={{ display: 'flex', alignItems: 'center', gap: 4, cursor: 'pointer', padding: '2px 4px', borderRadius: 3, background: open ? 'var(--hover)' : 'transparent' }} title="Switch vault">
|
||||
<FolderOpen size={13} />
|
||||
{activeVault?.label ?? 'Vault'}
|
||||
</span>
|
||||
{open && (
|
||||
<div style={{ position: 'absolute', bottom: '100%', left: 0, marginBottom: 4, background: 'var(--sidebar)', border: '1px solid var(--border)', borderRadius: 6, padding: 4, minWidth: 200, boxShadow: '0 4px 12px rgba(0,0,0,0.3)', zIndex: 1000 }}>
|
||||
{vaults.map((v) => <VaultMenuItem key={v.path} vault={v} isActive={v.path === vaultPath} onSelect={() => { onSwitchVault(v.path); setOpen(false) }} />)}
|
||||
<div style={{ height: 1, background: 'var(--border)', margin: '4px 0' }} />
|
||||
{onOpenLocalFolder && (
|
||||
<div
|
||||
role="button"
|
||||
onClick={() => { onOpenLocalFolder(); setOpen(false) }}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: '100%',
|
||||
left: 0,
|
||||
marginBottom: 4,
|
||||
background: 'var(--sidebar)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: 6,
|
||||
padding: 4,
|
||||
minWidth: 160,
|
||||
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
|
||||
zIndex: 1000,
|
||||
display: 'flex', alignItems: 'center', gap: 6, padding: '4px 8px', borderRadius: 4,
|
||||
cursor: 'pointer', background: 'transparent',
|
||||
color: 'var(--muted-foreground)', fontSize: 12,
|
||||
}}
|
||||
onMouseEnter={<span class="fstat-no" title="function not covered" >e => {</span> <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'var(--hover)' }</span>}
|
||||
onMouseLeave={<span class="fstat-no" title="function not covered" >e => {</span> <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'transparent' }</span>}
|
||||
data-testid="vault-menu-open-local"
|
||||
>
|
||||
{vaults.map(<span class="fstat-no" title="function not covered" >(v</span>) => (
|
||||
<span class="cstat-no" title="statement not covered" > <div</span>
|
||||
key={v.path}
|
||||
role="button"
|
||||
onClick={<span class="fstat-no" title="function not covered" >() => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onSwitchVault(v.path)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setShowVaultMenu(false)</span>
|
||||
}}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
padding: '4px 8px',
|
||||
borderRadius: 4,
|
||||
cursor: 'pointer',
|
||||
background: v.path === vaultPath ? 'var(--hover)' : 'transparent',
|
||||
color: v.path === vaultPath ? 'var(--foreground)' : 'var(--muted-foreground)',
|
||||
fontSize: 12,
|
||||
}}
|
||||
onMouseEnter={<span class="fstat-no" title="function not covered" >(e</span>) => {
|
||||
<span class="cstat-no" title="statement not covered" > if (v.path !== vaultPath) <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'var(--hover)'</span></span>
|
||||
}}
|
||||
onMouseLeave={<span class="fstat-no" title="function not covered" >(e</span>) => {
|
||||
<span class="cstat-no" title="statement not covered" > if (v.path !== vaultPath) <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'transparent'</span></span>
|
||||
}}
|
||||
>
|
||||
{v.path === vaultPath ? <Check size={12} /> : <span style={{ width: 12 }} />}
|
||||
{v.label}
|
||||
</div>
|
||||
))}
|
||||
<FolderOpen size={12} />
|
||||
Open local folder
|
||||
</div>
|
||||
)}
|
||||
{onConnectGitHub && (
|
||||
<div
|
||||
role="button"
|
||||
onClick={<span class="fstat-no" title="function not covered" >() => {</span> <span class="cstat-no" title="statement not covered" >onConnectGitHub(); <span class="cstat-no" title="statement not covered" >s</span>etOpen(false) }</span>}
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 6, padding: '4px 8px', borderRadius: 4,
|
||||
cursor: 'pointer', background: 'transparent',
|
||||
color: hasGitHub ? <span class="branch-0 cbranch-no" title="branch not covered" >'var(--muted-foreground)' : '</span>var(--accent-blue)', fontSize: 12,
|
||||
}}
|
||||
onMouseEnter={<span class="fstat-no" title="function not covered" >e => {</span> <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'var(--hover)' }</span>}
|
||||
onMouseLeave={<span class="fstat-no" title="function not covered" >e => {</span> <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'transparent' }</span>}
|
||||
data-testid="vault-menu-connect-github"
|
||||
>
|
||||
<Github size={12} />
|
||||
Connect GitHub repo
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<span style={{ color: 'var(--border)' }}>|</span>
|
||||
<span style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<Package size={13} />
|
||||
v0.4.2
|
||||
</span>
|
||||
<span style={{ color: 'var(--border)' }}>|</span>
|
||||
<span style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<GitBranch size={13} />
|
||||
main
|
||||
</span>
|
||||
<span style={{ color: 'var(--border)' }}>|</span>
|
||||
<span style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<RefreshCw size={13} style={{ color: 'var(--accent-green)' }} />
|
||||
Synced 2m ago
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{/* Right section */}
|
||||
const ICON_STYLE = { display: 'flex', alignItems: 'center', gap: 4 } as const
|
||||
const DISABLED_STYLE = { display: 'flex', alignItems: 'center', opacity: 0.4, cursor: 'not-allowed' } as const
|
||||
const SEP_STYLE = { color: 'var(--border)' } as const
|
||||
|
||||
export function StatusBar({ noteCount, modifiedCount = 0, vaultPath, vaults, onSwitchVault, onOpenSettings, onOpenLocalFolder, onConnectGitHub, onClickPending, hasGitHub }: StatusBarProps) {
|
||||
return (
|
||||
<footer style={{ height: 30, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'space-between', background: 'var(--sidebar)', borderTop: '1px solid var(--border)', padding: '0 8px', fontSize: 11, color: 'var(--muted-foreground)' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<span style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<Sparkles size={13} style={{ color: 'var(--accent-purple)' }} />
|
||||
Claude Sonnet 4
|
||||
</span>
|
||||
<span style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<FileText size={13} />
|
||||
{noteCount.toLocaleString()} notes
|
||||
</span>
|
||||
<VaultMenu vaults={vaults} vaultPath={vaultPath} onSwitchVault={onSwitchVault} onOpenLocalFolder={onOpenLocalFolder} onConnectGitHub={onConnectGitHub} hasGitHub={hasGitHub} />
|
||||
<span style={SEP_STYLE}>|</span>
|
||||
<span style={ICON_STYLE}><Package size={13} />v0.4.2</span>
|
||||
<span style={SEP_STYLE}>|</span>
|
||||
<span style={ICON_STYLE}><GitBranch size={13} />main</span>
|
||||
<span style={SEP_STYLE}>|</span>
|
||||
<span style={ICON_STYLE}><RefreshCw size={13} style={{ color: 'var(--accent-green)' }} />Synced 2m ago</span>
|
||||
{modifiedCount > 0 && (
|
||||
<>
|
||||
<span style={SEP_STYLE}>|</span>
|
||||
<span
|
||||
role="button"
|
||||
onClick={onClickPending}
|
||||
style={{ ...ICON_STYLE, cursor: 'pointer', padding: '2px 4px', borderRadius: 3, background: 'transparent' }}
|
||||
title="View pending changes"
|
||||
onMouseEnter={<span class="fstat-no" title="function not covered" >e => {</span> <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'var(--hover)' }</span>}
|
||||
onMouseLeave={<span class="fstat-no" title="function not covered" >e => {</span> <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'transparent' }</span>}
|
||||
data-testid="status-modified-count"
|
||||
><CircleDot size={13} style={{ color: 'var(--accent-orange)' }} />{modifiedCount} pending</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<span style={ICON_STYLE}><Sparkles size={13} style={{ color: 'var(--accent-purple)' }} />Claude Sonnet 4</span>
|
||||
<span style={ICON_STYLE}><FileText size={13} />{noteCount.toLocaleString()} notes</span>
|
||||
<span style={DISABLED_STYLE} title="Coming soon"><Bell size={14} /></span>
|
||||
<span
|
||||
style={{ display: 'flex', alignItems: 'center', opacity: 0.4, cursor: 'not-allowed' }}
|
||||
title="Coming soon"
|
||||
>
|
||||
<Bell size={14} />
|
||||
</span>
|
||||
<span
|
||||
style={{ display: 'flex', alignItems: 'center', opacity: 0.4, cursor: 'not-allowed' }}
|
||||
title="Coming soon"
|
||||
role="button"
|
||||
onClick={onOpenSettings}
|
||||
style={{ display: 'flex', alignItems: 'center', cursor: 'pointer', padding: '2px 4px', borderRadius: 3, background: 'transparent' }}
|
||||
title="Settings (⌘,)"
|
||||
onMouseEnter={<span class="fstat-no" title="function not covered" >e => {</span> <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'var(--hover)' }</span>}
|
||||
onMouseLeave={<span class="fstat-no" title="function not covered" >e => {</span> <span class="cstat-no" title="statement not covered" >e.currentTarget.style.background = 'transparent' }</span>}
|
||||
>
|
||||
<Settings size={14} />
|
||||
</span>
|
||||
@@ -547,7 +523,7 @@ export function StatusBar({ noteCount, vaultPath, vaults, onSwitchVault }: Statu
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -100,13 +100,13 @@
|
||||
<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">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">35x</span>
|
||||
<span class="cline-any cline-yes">35x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">35x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -154,7 +154,7 @@ export function Toast({ message, onDismiss }: ToastProps) {
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">21.05% </span>
|
||||
<span class="strong">95.83% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>4/19</span>
|
||||
<span class='fraction'>23/24</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">22.22% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>2/9</span>
|
||||
<span class='fraction'>10/10</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">18.18% </span>
|
||||
<span class="strong">92.3% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>2/11</span>
|
||||
<span class='fraction'>12/13</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">21.05% </span>
|
||||
<span class="strong">95.23% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>4/19</span>
|
||||
<span class='fraction'>20/21</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -186,29 +186,16 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<a name='L124'></a><a href='#L124'>124</a></td><td class="line-coverage quiet"><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-yes">10x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">572x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -221,100 +208,46 @@
|
||||
<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-yes">12x</span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">12x</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-yes">3x</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>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">105x</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-yes">8x</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-no"> </span>
|
||||
<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-neutral"> </span>
|
||||
<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-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-no"> </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-no"> </span>
|
||||
<span class="cline-any cline-yes">96x</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-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -324,7 +257,6 @@
|
||||
<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>
|
||||
@@ -333,7 +265,30 @@
|
||||
<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-yes">2x</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-yes">2867x</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-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -353,64 +308,17 @@
|
||||
<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></td><td class="text"><pre class="prettyprint lang-js">import { useState, type ComponentType } from 'react'
|
||||
import {
|
||||
FileText, Wrench, Flask, Target, ArrowsClockwise, Users, CalendarBlank,
|
||||
Tag, StackSimple, BookOpen, CookingPot, Heart, Star, House, Lightbulb,
|
||||
Briefcase, Gear, Cube, Leaf, MusicNote, Camera, Airplane, GameController,
|
||||
PaintBrush, ShoppingCart, GraduationCap, Trophy, ChatCircle, Notebook,
|
||||
MapPin, Code, Barbell, PawPrint, Pill, Knife,
|
||||
type IconProps,
|
||||
} from '@phosphor-icons/react'
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { useState, useMemo } from 'react'
|
||||
import { MagnifyingGlass } from '@phosphor-icons/react'
|
||||
import { ICON_OPTIONS, type IconEntry } from '../utils/iconRegistry'
|
||||
import { ACCENT_COLORS } from '../utils/typeColors'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
/** Curated Phosphor icons (normal weight) for type customization */
|
||||
export const ICON_OPTIONS: { name: string; Icon: ComponentType<IconProps> }[] = [
|
||||
{ name: 'file-text', Icon: FileText },
|
||||
{ name: 'wrench', Icon: Wrench },
|
||||
{ name: 'flask', Icon: Flask },
|
||||
{ name: 'target', Icon: Target },
|
||||
{ name: 'arrows-clockwise', Icon: ArrowsClockwise },
|
||||
{ name: 'users', Icon: Users },
|
||||
{ name: 'calendar-blank', Icon: CalendarBlank },
|
||||
{ name: 'tag', Icon: Tag },
|
||||
{ name: 'stack-simple', Icon: StackSimple },
|
||||
{ name: 'book-open', Icon: BookOpen },
|
||||
{ name: 'cooking-pot', Icon: CookingPot },
|
||||
{ name: 'heart', Icon: Heart },
|
||||
{ name: 'star', Icon: Star },
|
||||
{ name: 'house', Icon: House },
|
||||
{ name: 'lightbulb', Icon: Lightbulb },
|
||||
{ name: 'briefcase', Icon: Briefcase },
|
||||
{ name: 'gear', Icon: Gear },
|
||||
{ name: 'cube', Icon: Cube },
|
||||
{ name: 'leaf', Icon: Leaf },
|
||||
{ name: 'music-note', Icon: MusicNote },
|
||||
{ name: 'camera', Icon: Camera },
|
||||
{ name: 'airplane', Icon: Airplane },
|
||||
{ name: 'game-controller', Icon: GameController },
|
||||
{ name: 'paint-brush', Icon: PaintBrush },
|
||||
{ name: 'shopping-cart', Icon: ShoppingCart },
|
||||
{ name: 'graduation-cap', Icon: GraduationCap },
|
||||
{ name: 'trophy', Icon: Trophy },
|
||||
{ name: 'chat-circle', Icon: ChatCircle },
|
||||
{ name: 'notebook', Icon: Notebook },
|
||||
{ name: 'map-pin', Icon: MapPin },
|
||||
{ name: 'code', Icon: Code },
|
||||
{ name: 'barbell', Icon: Barbell },
|
||||
{ name: 'paw-print', Icon: PawPrint },
|
||||
{ name: 'pill', Icon: Pill },
|
||||
{ name: 'knife', Icon: Knife },
|
||||
]
|
||||
|
||||
const ICON_MAP: Record<string, ComponentType<IconProps>> = Object.fromEntries(
|
||||
ICON_OPTIONS.map((o) => [o.name, o.Icon]),
|
||||
)
|
||||
|
||||
/** Resolves a Phosphor icon name to its component, with fallback to FileText */
|
||||
export function resolveIcon(name: string | null): ComponentType<IconProps> {
|
||||
return (name && <span class="branch-1 cbranch-no" title="branch not covered" >ICON_MAP[name]) || F</span>ileText
|
||||
function filterIcons(icons: IconEntry[], query: string): IconEntry[] {
|
||||
if (!query) return icons
|
||||
const lower = query.toLowerCase()
|
||||
return icons.filter((o) => o.name.includes(lower))
|
||||
}
|
||||
|
||||
interface TypeCustomizePopoverProps {
|
||||
@@ -421,45 +329,48 @@ interface TypeCustomizePopoverProps {
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export function <span class="fstat-no" title="function not covered" >TypeCustomizePopover({</span>
|
||||
export function TypeCustomizePopover({
|
||||
currentIcon,
|
||||
currentColor,
|
||||
onChangeIcon,
|
||||
onChangeColor,
|
||||
onClose,
|
||||
}: TypeCustomizePopoverProps) {
|
||||
const [selectedColor, setSelectedColor<span class="cstat-no" title="statement not covered" >] = useState(currentColor)</span>
|
||||
const [selectedIcon, setSelectedIcon<span class="cstat-no" title="statement not covered" >] = useState(currentIcon)</span>
|
||||
const [selectedColor, setSelectedColor] = useState(currentColor)
|
||||
const [selectedIcon, setSelectedIcon] = useState(currentIcon)
|
||||
const [search, setSearch] = useState('')
|
||||
|
||||
const handleColorClick = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >(k</span>ey: string) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > setSelectedColor(key)</span>
|
||||
<span class="cstat-no" title="statement not covered" > onChangeColor(key)</span>
|
||||
const filteredIcons = useMemo(() => filterIcons(ICON_OPTIONS, search), [search])
|
||||
|
||||
const handleColorClick = (key: string) => {
|
||||
setSelectedColor(key)
|
||||
onChangeColor(key)
|
||||
}
|
||||
|
||||
const handleIconClick = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >(n</span>ame: string) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > setSelectedIcon(name)</span>
|
||||
<span class="cstat-no" title="statement not covered" > onChangeIcon(name)</span>
|
||||
const handleIconClick = (name: string) => {
|
||||
setSelectedIcon(name)
|
||||
onChangeIcon(name)
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
return (
|
||||
<div
|
||||
className="bg-popover text-popover-foreground z-50 rounded-lg border shadow-md"
|
||||
style={{ width: 264, padding: 12 }}
|
||||
onClick={<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >e.stopPropagation()}</span>
|
||||
style={{ width: 280, padding: 12 }}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
onContextMenu={<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >e.stopPropagation()}</span>
|
||||
>
|
||||
{/* Color section */}
|
||||
<div className="font-mono-overline mb-2 text-muted-foreground">COLOR</div>
|
||||
<div className="flex gap-2 mb-3">
|
||||
{ACCENT_COLORS.map(<span class="fstat-no" title="function not covered" >(c</span>) => (
|
||||
<span class="cstat-no" title="statement not covered" > <button</span>
|
||||
<div className="flex gap-2 mb-3 flex-wrap">
|
||||
{ACCENT_COLORS.map((c) => (
|
||||
<button
|
||||
key={c.key}
|
||||
className={cn(
|
||||
"flex items-center justify-center rounded-full border-2 cursor-pointer transition-all",
|
||||
selectedColor === c.key ? "border-foreground scale-110" : "border-transparent hover:scale-105",
|
||||
)}
|
||||
style={{ width: 28, height: 28, backgroundColor: c.css, border: selectedColor === c.key ? '2px solid var(--foreground)' : '2px solid transparent' }}
|
||||
onClick={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >h</span>andleColorClick(c.key)}</span>
|
||||
style={{ width: 24, height: 24, backgroundColor: c.css, border: selectedColor === c.key ? '2px solid var(--foreground)' : '2px solid transparent' }}
|
||||
onClick={() => handleColorClick(c.key)}
|
||||
title={c.label}
|
||||
/>
|
||||
))}
|
||||
@@ -467,23 +378,46 @@ export function <span class="fstat-no" title="function not covered" >TypeCustomi
|
||||
|
||||
{/* Icon section */}
|
||||
<div className="font-mono-overline mb-2 text-muted-foreground">ICON</div>
|
||||
<div className="flex flex-wrap gap-1" style={{ maxHeight: 200, overflowY: 'auto' }}>
|
||||
{ICON_OPTIONS.map(<span class="fstat-no" title="function not covered" >({</span> name, Icon }) => (
|
||||
<span class="cstat-no" title="statement not covered" > <button</span>
|
||||
key={name}
|
||||
className={cn(
|
||||
"flex items-center justify-center rounded cursor-pointer transition-colors",
|
||||
selectedIcon === name
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-muted-foreground hover:bg-accent hover:text-foreground",
|
||||
)}
|
||||
style={{ width: 30, height: 30 }}
|
||||
onClick={<span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >h</span>andleIconClick(name)}</span>
|
||||
title={name}
|
||||
>
|
||||
<Icon size={16} />
|
||||
</button>
|
||||
))}
|
||||
|
||||
{/* Search input */}
|
||||
<div className="relative mb-2">
|
||||
<MagnifyingGlass
|
||||
size={14}
|
||||
className="absolute left-2 top-1/2 -translate-y-1/2 text-muted-foreground pointer-events-none"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
placeholder="Search icons…"
|
||||
className="w-full rounded border border-border bg-background pl-7 pr-2 py-1 text-[12px] text-foreground placeholder:text-muted-foreground outline-none focus:border-primary"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Icon grid */}
|
||||
<div className="flex flex-wrap gap-1 overflow-y-auto" style={{ maxHeight: 240 }}>
|
||||
{filteredIcons.length === 0 ? (
|
||||
<div className="w-full py-6 text-center text-[12px] text-muted-foreground">
|
||||
No icons found
|
||||
</div>
|
||||
) : (
|
||||
filteredIcons.map(({ name, Icon }) => (
|
||||
<button
|
||||
key={name}
|
||||
className={cn(
|
||||
"flex items-center justify-center rounded cursor-pointer transition-colors",
|
||||
selectedIcon === name
|
||||
? "bg-primary/10 text-primary"
|
||||
: "text-muted-foreground hover:bg-accent hover:text-foreground",
|
||||
)}
|
||||
style={{ width: 30, height: 30 }}
|
||||
onClick={() => handleIconClick(name)}
|
||||
title={name}
|
||||
>
|
||||
<Icon size={16} />
|
||||
</button>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Done button */}
|
||||
@@ -505,7 +439,7 @@ export function <span class="fstat-no" title="function not covered" >TypeCustomi
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">54.44% </span>
|
||||
<span class="strong">80.93% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>710/1304</span>
|
||||
<span class='fraction'>1482/1831</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">50.75% </span>
|
||||
<span class="strong">73.93% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>540/1064</span>
|
||||
<span class='fraction'>990/1339</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">50.11% </span>
|
||||
<span class="strong">80.36% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>210/419</span>
|
||||
<span class='fraction'>491/611</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">57.02% </span>
|
||||
<span class="strong">82.93% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>629/1103</span>
|
||||
<span class='fraction'>1249/1506</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line medium'></div>
|
||||
<div class='status-line high'></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
@@ -79,29 +79,14 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td class="file low" data-value="AIChatPanel.tsx"><a href="AIChatPanel.tsx.html">AIChatPanel.tsx</a></td>
|
||||
<td data-value="1.17" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 1%"></div><div class="cover-empty" style="width: 99%"></div></div>
|
||||
</td>
|
||||
<td data-value="1.17" class="pct low">1.17%</td>
|
||||
<td data-value="85" class="abs low">1/85</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="49" class="abs low">0/49</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="55" class="abs low">0/55</td>
|
||||
<td data-value="1.47" class="pct low">1.47%</td>
|
||||
<td data-value="68" class="abs low">1/68</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="BreadcrumbBar.tsx"><a href="BreadcrumbBar.tsx.html">BreadcrumbBar.tsx</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="4" class="abs high">4/4</td>
|
||||
<td data-value="69.23" class="pct medium">69.23%</td>
|
||||
<td data-value="26" class="abs medium">18/26</td>
|
||||
<td data-value="71.42" class="pct medium">71.42%</td>
|
||||
<td data-value="28" class="abs medium">20/28</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="2" class="abs high">2/2</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
@@ -109,33 +94,48 @@
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="CommitDialog.tsx"><a href="CommitDialog.tsx.html">CommitDialog.tsx</a></td>
|
||||
<td data-value="31.81" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 31%"></div><div class="cover-empty" style="width: 69%"></div></div>
|
||||
<td class="file high" data-value="CommandPalette.tsx"><a href="CommandPalette.tsx.html">CommandPalette.tsx</a></td>
|
||||
<td data-value="96.73" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 96%"></div><div class="cover-empty" style="width: 4%"></div></div>
|
||||
</td>
|
||||
<td data-value="31.81" class="pct low">31.81%</td>
|
||||
<td data-value="22" class="abs low">7/22</td>
|
||||
<td data-value="13.33" class="pct low">13.33%</td>
|
||||
<td data-value="15" class="abs low">2/15</td>
|
||||
<td data-value="28.57" class="pct low">28.57%</td>
|
||||
<td data-value="7" class="abs low">2/7</td>
|
||||
<td data-value="36.84" class="pct low">36.84%</td>
|
||||
<td data-value="19" class="abs low">7/19</td>
|
||||
<td data-value="96.73" class="pct high">96.73%</td>
|
||||
<td data-value="92" class="abs high">89/92</td>
|
||||
<td data-value="88.88" class="pct high">88.88%</td>
|
||||
<td data-value="36" class="abs high">32/36</td>
|
||||
<td data-value="93.54" class="pct high">93.54%</td>
|
||||
<td data-value="31" class="abs high">29/31</td>
|
||||
<td data-value="97.14" class="pct high">97.14%</td>
|
||||
<td data-value="70" class="abs high">68/70</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="CreateNoteDialog.tsx"><a href="CreateNoteDialog.tsx.html">CreateNoteDialog.tsx</a></td>
|
||||
<td data-value="36" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 36%"></div><div class="cover-empty" style="width: 64%"></div></div>
|
||||
<td class="file high" data-value="CommitDialog.tsx"><a href="CommitDialog.tsx.html">CommitDialog.tsx</a></td>
|
||||
<td data-value="95.45" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 95%"></div><div class="cover-empty" style="width: 5%"></div></div>
|
||||
</td>
|
||||
<td data-value="36" class="pct low">36%</td>
|
||||
<td data-value="25" class="abs low">9/25</td>
|
||||
<td data-value="35.29" class="pct low">35.29%</td>
|
||||
<td data-value="17" class="abs low">6/17</td>
|
||||
<td data-value="30" class="pct low">30%</td>
|
||||
<td data-value="10" class="abs low">3/10</td>
|
||||
<td data-value="40.9" class="pct low">40.9%</td>
|
||||
<td data-value="22" class="abs low">9/22</td>
|
||||
<td data-value="95.45" class="pct high">95.45%</td>
|
||||
<td data-value="22" class="abs high">21/22</td>
|
||||
<td data-value="73.33" class="pct medium">73.33%</td>
|
||||
<td data-value="15" class="abs medium">11/15</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="7" class="abs high">7/7</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="19" class="abs high">19/19</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="CreateNoteDialog.tsx"><a href="CreateNoteDialog.tsx.html">CreateNoteDialog.tsx</a></td>
|
||||
<td data-value="92" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 92%"></div><div class="cover-empty" style="width: 8%"></div></div>
|
||||
</td>
|
||||
<td data-value="92" class="pct high">92%</td>
|
||||
<td data-value="25" class="abs high">23/25</td>
|
||||
<td data-value="88.23" class="pct high">88.23%</td>
|
||||
<td data-value="17" class="abs high">15/17</td>
|
||||
<td data-value="90" class="pct high">90%</td>
|
||||
<td data-value="10" class="abs high">9/10</td>
|
||||
<td data-value="95.23" class="pct high">95.23%</td>
|
||||
<td data-value="21" class="abs high">20/21</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -154,168 +154,303 @@
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="DiffView.tsx"><a href="DiffView.tsx.html">DiffView.tsx</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
<td class="file high" data-value="DiffView.tsx"><a href="DiffView.tsx.html">DiffView.tsx</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="14" class="abs low">0/14</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="21" class="abs low">0/21</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="2" class="abs low">0/2</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="14" class="abs low">0/14</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="16" class="abs high">16/16</td>
|
||||
<td data-value="93.75" class="pct high">93.75%</td>
|
||||
<td data-value="16" class="abs high">15/16</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="5" class="abs high">5/5</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="11" class="abs high">11/11</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="DynamicPropertiesPanel.tsx"><a href="DynamicPropertiesPanel.tsx.html">DynamicPropertiesPanel.tsx</a></td>
|
||||
<td data-value="41.96" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 41%"></div><div class="cover-empty" style="width: 59%"></div></div>
|
||||
<td class="file high" data-value="DynamicPropertiesPanel.tsx"><a href="DynamicPropertiesPanel.tsx.html">DynamicPropertiesPanel.tsx</a></td>
|
||||
<td data-value="87.78" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 87%"></div><div class="cover-empty" style="width: 13%"></div></div>
|
||||
</td>
|
||||
<td data-value="41.96" class="pct low">41.96%</td>
|
||||
<td data-value="112" class="abs low">47/112</td>
|
||||
<td data-value="40.24" class="pct low">40.24%</td>
|
||||
<td data-value="82" class="abs low">33/82</td>
|
||||
<td data-value="27.77" class="pct low">27.77%</td>
|
||||
<td data-value="36" class="abs low">10/36</td>
|
||||
<td data-value="47.19" class="pct low">47.19%</td>
|
||||
<td data-value="89" class="abs low">42/89</td>
|
||||
<td data-value="87.78" class="pct high">87.78%</td>
|
||||
<td data-value="131" class="abs high">115/131</td>
|
||||
<td data-value="80.73" class="pct high">80.73%</td>
|
||||
<td data-value="109" class="abs high">88/109</td>
|
||||
<td data-value="90.38" class="pct high">90.38%</td>
|
||||
<td data-value="52" class="abs high">47/52</td>
|
||||
<td data-value="93.68" class="pct high">93.68%</td>
|
||||
<td data-value="95" class="abs high">89/95</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="EditableValue.tsx"><a href="EditableValue.tsx.html">EditableValue.tsx</a></td>
|
||||
<td data-value="24.59" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 24%"></div><div class="cover-empty" style="width: 76%"></div></div>
|
||||
<td class="file high" data-value="EditableValue.tsx"><a href="EditableValue.tsx.html">EditableValue.tsx</a></td>
|
||||
<td data-value="97.72" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 97%"></div><div class="cover-empty" style="width: 3%"></div></div>
|
||||
</td>
|
||||
<td data-value="24.59" class="pct low">24.59%</td>
|
||||
<td data-value="61" class="abs low">15/61</td>
|
||||
<td data-value="16.66" class="pct low">16.66%</td>
|
||||
<td data-value="30" class="abs low">5/30</td>
|
||||
<td data-value="15" class="pct low">15%</td>
|
||||
<td data-value="20" class="abs low">3/20</td>
|
||||
<td data-value="26.31" class="pct low">26.31%</td>
|
||||
<td data-value="57" class="abs low">15/57</td>
|
||||
<td data-value="97.72" class="pct high">97.72%</td>
|
||||
<td data-value="88" class="abs high">86/88</td>
|
||||
<td data-value="82.5" class="pct high">82.5%</td>
|
||||
<td data-value="40" class="abs high">33/40</td>
|
||||
<td data-value="92.85" class="pct high">92.85%</td>
|
||||
<td data-value="28" class="abs high">26/28</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="82" class="abs high">82/82</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="Editor.tsx"><a href="Editor.tsx.html">Editor.tsx</a></td>
|
||||
<td data-value="50.52" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 50%"></div><div class="cover-empty" style="width: 50%"></div></div>
|
||||
<td data-value="61.81" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 61%"></div><div class="cover-empty" style="width: 39%"></div></div>
|
||||
</td>
|
||||
<td data-value="50.52" class="pct medium">50.52%</td>
|
||||
<td data-value="192" class="abs medium">97/192</td>
|
||||
<td data-value="48.33" class="pct low">48.33%</td>
|
||||
<td data-value="120" class="abs low">58/120</td>
|
||||
<td data-value="47.36" class="pct low">47.36%</td>
|
||||
<td data-value="38" class="abs low">18/38</td>
|
||||
<td data-value="53.84" class="pct medium">53.84%</td>
|
||||
<td data-value="169" class="abs medium">91/169</td>
|
||||
<td data-value="61.81" class="pct medium">61.81%</td>
|
||||
<td data-value="220" class="abs medium">136/220</td>
|
||||
<td data-value="55.83" class="pct medium">55.83%</td>
|
||||
<td data-value="120" class="abs medium">67/120</td>
|
||||
<td data-value="62.5" class="pct medium">62.5%</td>
|
||||
<td data-value="48" class="abs medium">30/48</td>
|
||||
<td data-value="63.73" class="pct medium">63.73%</td>
|
||||
<td data-value="193" class="abs medium">123/193</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="Inspector.tsx"><a href="Inspector.tsx.html">Inspector.tsx</a></td>
|
||||
<td data-value="64.28" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 64%"></div><div class="cover-empty" style="width: 36%"></div></div>
|
||||
<td class="file medium" data-value="GitHubVaultModal.tsx"><a href="GitHubVaultModal.tsx.html">GitHubVaultModal.tsx</a></td>
|
||||
<td data-value="70" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 70%"></div><div class="cover-empty" style="width: 30%"></div></div>
|
||||
</td>
|
||||
<td data-value="64.28" class="pct medium">64.28%</td>
|
||||
<td data-value="140" class="abs medium">90/140</td>
|
||||
<td data-value="52.17" class="pct medium">52.17%</td>
|
||||
<td data-value="161" class="abs medium">84/161</td>
|
||||
<td data-value="72.09" class="pct medium">72.09%</td>
|
||||
<td data-value="43" class="abs medium">31/43</td>
|
||||
<td data-value="76.19" class="pct medium">76.19%</td>
|
||||
<td data-value="105" class="abs medium">80/105</td>
|
||||
<td data-value="70" class="pct medium">70%</td>
|
||||
<td data-value="100" class="abs medium">70/100</td>
|
||||
<td data-value="53.84" class="pct medium">53.84%</td>
|
||||
<td data-value="78" class="abs medium">42/78</td>
|
||||
<td data-value="62.96" class="pct medium">62.96%</td>
|
||||
<td data-value="27" class="abs medium">17/27</td>
|
||||
<td data-value="75" class="pct medium">75%</td>
|
||||
<td data-value="92" class="abs medium">69/92</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="Inspector.tsx"><a href="Inspector.tsx.html">Inspector.tsx</a></td>
|
||||
<td data-value="88" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 88%"></div><div class="cover-empty" style="width: 12%"></div></div>
|
||||
</td>
|
||||
<td data-value="88" class="pct high">88%</td>
|
||||
<td data-value="50" class="abs high">44/50</td>
|
||||
<td data-value="59.25" class="pct medium">59.25%</td>
|
||||
<td data-value="54" class="abs medium">32/54</td>
|
||||
<td data-value="78.57" class="pct medium">78.57%</td>
|
||||
<td data-value="14" class="abs medium">11/14</td>
|
||||
<td data-value="92.3" class="pct high">92.3%</td>
|
||||
<td data-value="39" class="abs high">36/39</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="InspectorPanels.tsx"><a href="InspectorPanels.tsx.html">InspectorPanels.tsx</a></td>
|
||||
<td data-value="85.41" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 85%"></div><div class="cover-empty" style="width: 15%"></div></div>
|
||||
</td>
|
||||
<td data-value="85.41" class="pct high">85.41%</td>
|
||||
<td data-value="192" class="abs high">164/192</td>
|
||||
<td data-value="80.5" class="pct high">80.5%</td>
|
||||
<td data-value="159" class="abs high">128/159</td>
|
||||
<td data-value="86.36" class="pct high">86.36%</td>
|
||||
<td data-value="66" class="abs high">57/66</td>
|
||||
<td data-value="91.03" class="pct high">91.03%</td>
|
||||
<td data-value="145" class="abs high">132/145</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="NoteAutocomplete.tsx"><a href="NoteAutocomplete.tsx.html">NoteAutocomplete.tsx</a></td>
|
||||
<td data-value="75" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 75%"></div><div class="cover-empty" style="width: 25%"></div></div>
|
||||
</td>
|
||||
<td data-value="75" class="pct medium">75%</td>
|
||||
<td data-value="72" class="abs medium">54/72</td>
|
||||
<td data-value="74.5" class="pct medium">74.5%</td>
|
||||
<td data-value="51" class="abs medium">38/51</td>
|
||||
<td data-value="75" class="pct medium">75%</td>
|
||||
<td data-value="20" class="abs medium">15/20</td>
|
||||
<td data-value="74.6" class="pct medium">74.6%</td>
|
||||
<td data-value="63" class="abs medium">47/63</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="NoteItem.tsx"><a href="NoteItem.tsx.html">NoteItem.tsx</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="19" class="abs high">19/19</td>
|
||||
<td data-value="94.73" class="pct high">94.73%</td>
|
||||
<td data-value="38" class="abs high">36/38</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="6" class="abs high">6/6</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="17" class="abs high">17/17</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="NoteList.tsx"><a href="NoteList.tsx.html">NoteList.tsx</a></td>
|
||||
<td data-value="85.71" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 85%"></div><div class="cover-empty" style="width: 15%"></div></div>
|
||||
<td data-value="93.6" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 93%"></div><div class="cover-empty" style="width: 7%"></div></div>
|
||||
</td>
|
||||
<td data-value="85.71" class="pct high">85.71%</td>
|
||||
<td data-value="245" class="abs high">210/245</td>
|
||||
<td data-value="76.49" class="pct medium">76.49%</td>
|
||||
<td data-value="251" class="abs medium">192/251</td>
|
||||
<td data-value="85.52" class="pct high">85.52%</td>
|
||||
<td data-value="76" class="abs high">65/76</td>
|
||||
<td data-value="87.5" class="pct high">87.5%</td>
|
||||
<td data-value="208" class="abs high">182/208</td>
|
||||
<td data-value="93.6" class="pct high">93.6%</td>
|
||||
<td data-value="125" class="abs high">117/125</td>
|
||||
<td data-value="83.78" class="pct high">83.78%</td>
|
||||
<td data-value="111" class="abs high">93/111</td>
|
||||
<td data-value="93.47" class="pct high">93.47%</td>
|
||||
<td data-value="46" class="abs high">43/46</td>
|
||||
<td data-value="97.8" class="pct high">97.8%</td>
|
||||
<td data-value="91" class="abs high">89/91</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="QuickOpenPalette.tsx"><a href="QuickOpenPalette.tsx.html">QuickOpenPalette.tsx</a></td>
|
||||
<td data-value="27.02" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 27%"></div><div class="cover-empty" style="width: 73%"></div></div>
|
||||
<td class="file high" data-value="NoteSearchList.tsx"><a href="NoteSearchList.tsx.html">NoteSearchList.tsx</a></td>
|
||||
<td data-value="91.66" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 91%"></div><div class="cover-empty" style="width: 9%"></div></div>
|
||||
</td>
|
||||
<td data-value="27.02" class="pct low">27.02%</td>
|
||||
<td data-value="74" class="abs low">20/74</td>
|
||||
<td data-value="17.07" class="pct low">17.07%</td>
|
||||
<td data-value="41" class="abs low">7/41</td>
|
||||
<td data-value="31.81" class="pct low">31.81%</td>
|
||||
<td data-value="22" class="abs low">7/22</td>
|
||||
<td data-value="25.39" class="pct low">25.39%</td>
|
||||
<td data-value="63" class="abs low">16/63</td>
|
||||
<td data-value="91.66" class="pct high">91.66%</td>
|
||||
<td data-value="12" class="abs high">11/12</td>
|
||||
<td data-value="90.9" class="pct high">90.9%</td>
|
||||
<td data-value="11" class="abs high">10/11</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="5" class="abs high">5/5</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="11" class="abs high">11/11</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="ResizeHandle.tsx"><a href="ResizeHandle.tsx.html">ResizeHandle.tsx</a></td>
|
||||
<td data-value="35.71" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 35%"></div><div class="cover-empty" style="width: 65%"></div></div>
|
||||
<td class="file high" data-value="QuickOpenPalette.tsx"><a href="QuickOpenPalette.tsx.html">QuickOpenPalette.tsx</a></td>
|
||||
<td data-value="97.14" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 97%"></div><div class="cover-empty" style="width: 3%"></div></div>
|
||||
</td>
|
||||
<td data-value="35.71" class="pct low">35.71%</td>
|
||||
<td data-value="42" class="abs low">15/42</td>
|
||||
<td data-value="7.14" class="pct low">7.14%</td>
|
||||
<td data-value="14" class="abs low">1/14</td>
|
||||
<td data-value="42.85" class="pct low">42.85%</td>
|
||||
<td data-value="7" class="abs low">3/7</td>
|
||||
<td data-value="37.5" class="pct low">37.5%</td>
|
||||
<td data-value="40" class="abs low">15/40</td>
|
||||
<td data-value="97.14" class="pct high">97.14%</td>
|
||||
<td data-value="35" class="abs high">34/35</td>
|
||||
<td data-value="91.66" class="pct high">91.66%</td>
|
||||
<td data-value="12" class="abs high">11/12</td>
|
||||
<td data-value="90.9" class="pct high">90.9%</td>
|
||||
<td data-value="11" class="abs high">10/11</td>
|
||||
<td data-value="96.77" class="pct high">96.77%</td>
|
||||
<td data-value="31" class="abs high">30/31</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="ResizeHandle.tsx"><a href="ResizeHandle.tsx.html">ResizeHandle.tsx</a></td>
|
||||
<td data-value="97.61" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 97%"></div><div class="cover-empty" style="width: 3%"></div></div>
|
||||
</td>
|
||||
<td data-value="97.61" class="pct high">97.61%</td>
|
||||
<td data-value="42" class="abs high">41/42</td>
|
||||
<td data-value="78.57" class="pct medium">78.57%</td>
|
||||
<td data-value="14" class="abs medium">11/14</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="7" class="abs high">7/7</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="40" class="abs high">40/40</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="SearchPanel.tsx"><a href="SearchPanel.tsx.html">SearchPanel.tsx</a></td>
|
||||
<td data-value="88.88" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 88%"></div><div class="cover-empty" style="width: 12%"></div></div>
|
||||
</td>
|
||||
<td data-value="88.88" class="pct high">88.88%</td>
|
||||
<td data-value="54" class="abs high">48/54</td>
|
||||
<td data-value="86.95" class="pct high">86.95%</td>
|
||||
<td data-value="46" class="abs high">40/46</td>
|
||||
<td data-value="78.94" class="pct medium">78.94%</td>
|
||||
<td data-value="19" class="abs medium">15/19</td>
|
||||
<td data-value="88.37" class="pct high">88.37%</td>
|
||||
<td data-value="43" class="abs high">38/43</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="SettingsPanel.tsx"><a href="SettingsPanel.tsx.html">SettingsPanel.tsx</a></td>
|
||||
<td data-value="69.42" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 69%"></div><div class="cover-empty" style="width: 31%"></div></div>
|
||||
</td>
|
||||
<td data-value="69.42" class="pct medium">69.42%</td>
|
||||
<td data-value="121" class="abs medium">84/121</td>
|
||||
<td data-value="68.29" class="pct medium">68.29%</td>
|
||||
<td data-value="82" class="abs medium">56/82</td>
|
||||
<td data-value="72.97" class="pct medium">72.97%</td>
|
||||
<td data-value="37" class="abs medium">27/37</td>
|
||||
<td data-value="69.09" class="pct medium">69.09%</td>
|
||||
<td data-value="110" class="abs medium">76/110</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="Sidebar.tsx"><a href="Sidebar.tsx.html">Sidebar.tsx</a></td>
|
||||
<td data-value="66.07" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 66%"></div><div class="cover-empty" style="width: 34%"></div></div>
|
||||
<td data-value="68.79" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 68%"></div><div class="cover-empty" style="width: 32%"></div></div>
|
||||
</td>
|
||||
<td data-value="66.07" class="pct medium">66.07%</td>
|
||||
<td data-value="168" class="abs medium">111/168</td>
|
||||
<td data-value="55.92" class="pct medium">55.92%</td>
|
||||
<td data-value="152" class="abs medium">85/152</td>
|
||||
<td data-value="72.41" class="pct medium">72.41%</td>
|
||||
<td data-value="58" class="abs medium">42/58</td>
|
||||
<td data-value="65.69" class="pct medium">65.69%</td>
|
||||
<td data-value="137" class="abs medium">90/137</td>
|
||||
<td data-value="68.79" class="pct medium">68.79%</td>
|
||||
<td data-value="141" class="abs medium">97/141</td>
|
||||
<td data-value="52.43" class="pct medium">52.43%</td>
|
||||
<td data-value="82" class="abs medium">43/82</td>
|
||||
<td data-value="70.37" class="pct medium">70.37%</td>
|
||||
<td data-value="54" class="abs medium">38/54</td>
|
||||
<td data-value="70.75" class="pct medium">70.75%</td>
|
||||
<td data-value="106" class="abs medium">75/106</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="StatusBar.tsx"><a href="StatusBar.tsx.html">StatusBar.tsx</a></td>
|
||||
<td data-value="34.78" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 34%"></div><div class="cover-empty" style="width: 66%"></div></div>
|
||||
<td class="file high" data-value="SidebarParts.tsx"><a href="SidebarParts.tsx.html">SidebarParts.tsx</a></td>
|
||||
<td data-value="90" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 90%"></div><div class="cover-empty" style="width: 10%"></div></div>
|
||||
</td>
|
||||
<td data-value="34.78" class="pct low">34.78%</td>
|
||||
<td data-value="23" class="abs low">8/23</td>
|
||||
<td data-value="18.18" class="pct low">18.18%</td>
|
||||
<td data-value="22" class="abs low">4/22</td>
|
||||
<td data-value="27.27" class="pct low">27.27%</td>
|
||||
<td data-value="11" class="abs low">3/11</td>
|
||||
<td data-value="35.29" class="pct low">35.29%</td>
|
||||
<td data-value="17" class="abs low">6/17</td>
|
||||
<td data-value="90" class="pct high">90%</td>
|
||||
<td data-value="40" class="abs high">36/40</td>
|
||||
<td data-value="82.69" class="pct high">82.69%</td>
|
||||
<td data-value="52" class="abs high">43/52</td>
|
||||
<td data-value="95" class="pct high">95%</td>
|
||||
<td data-value="20" class="abs high">19/20</td>
|
||||
<td data-value="87.5" class="pct high">87.5%</td>
|
||||
<td data-value="32" class="abs high">28/32</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="TabBar.tsx"><a href="TabBar.tsx.html">TabBar.tsx</a></td>
|
||||
<td data-value="92.15" class="pic high">
|
||||
<td class="file high" data-value="SortDropdown.tsx"><a href="SortDropdown.tsx.html">SortDropdown.tsx</a></td>
|
||||
<td data-value="92.3" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 92%"></div><div class="cover-empty" style="width: 8%"></div></div>
|
||||
</td>
|
||||
<td data-value="92.15" class="pct high">92.15%</td>
|
||||
<td data-value="51" class="abs high">47/51</td>
|
||||
<td data-value="79.54" class="pct medium">79.54%</td>
|
||||
<td data-value="44" class="abs medium">35/44</td>
|
||||
<td data-value="91.66" class="pct high">91.66%</td>
|
||||
<td data-value="12" class="abs high">11/12</td>
|
||||
<td data-value="92.15" class="pct high">92.15%</td>
|
||||
<td data-value="51" class="abs high">47/51</td>
|
||||
<td data-value="92.3" class="pct high">92.3%</td>
|
||||
<td data-value="26" class="abs high">24/26</td>
|
||||
<td data-value="78.57" class="pct medium">78.57%</td>
|
||||
<td data-value="28" class="abs medium">22/28</td>
|
||||
<td data-value="90.9" class="pct high">90.9%</td>
|
||||
<td data-value="11" class="abs high">10/11</td>
|
||||
<td data-value="94.73" class="pct high">94.73%</td>
|
||||
<td data-value="19" class="abs high">18/19</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="StatusBar.tsx"><a href="StatusBar.tsx.html">StatusBar.tsx</a></td>
|
||||
<td data-value="65" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 65%"></div><div class="cover-empty" style="width: 35%"></div></div>
|
||||
</td>
|
||||
<td data-value="65" class="pct medium">65%</td>
|
||||
<td data-value="40" class="abs medium">26/40</td>
|
||||
<td data-value="80.64" class="pct high">80.64%</td>
|
||||
<td data-value="31" class="abs high">25/31</td>
|
||||
<td data-value="52.17" class="pct medium">52.17%</td>
|
||||
<td data-value="23" class="abs medium">12/23</td>
|
||||
<td data-value="62.06" class="pct medium">62.06%</td>
|
||||
<td data-value="29" class="abs medium">18/29</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="TabBar.tsx"><a href="TabBar.tsx.html">TabBar.tsx</a></td>
|
||||
<td data-value="67.92" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 67%"></div><div class="cover-empty" style="width: 33%"></div></div>
|
||||
</td>
|
||||
<td data-value="67.92" class="pct medium">67.92%</td>
|
||||
<td data-value="106" class="abs medium">72/106</td>
|
||||
<td data-value="68" class="pct medium">68%</td>
|
||||
<td data-value="75" class="abs medium">51/75</td>
|
||||
<td data-value="63.88" class="pct medium">63.88%</td>
|
||||
<td data-value="36" class="abs medium">23/36</td>
|
||||
<td data-value="70.52" class="pct medium">70.52%</td>
|
||||
<td data-value="95" class="abs medium">67/95</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -334,18 +469,48 @@
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="TypeCustomizePopover.tsx"><a href="TypeCustomizePopover.tsx.html">TypeCustomizePopover.tsx</a></td>
|
||||
<td data-value="21.05" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 21%"></div><div class="cover-empty" style="width: 79%"></div></div>
|
||||
<td class="file high" data-value="TypeCustomizePopover.tsx"><a href="TypeCustomizePopover.tsx.html">TypeCustomizePopover.tsx</a></td>
|
||||
<td data-value="95.83" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 95%"></div><div class="cover-empty" style="width: 5%"></div></div>
|
||||
</td>
|
||||
<td data-value="21.05" class="pct low">21.05%</td>
|
||||
<td data-value="19" class="abs low">4/19</td>
|
||||
<td data-value="22.22" class="pct low">22.22%</td>
|
||||
<td data-value="9" class="abs low">2/9</td>
|
||||
<td data-value="18.18" class="pct low">18.18%</td>
|
||||
<td data-value="11" class="abs low">2/11</td>
|
||||
<td data-value="21.05" class="pct low">21.05%</td>
|
||||
<td data-value="19" class="abs low">4/19</td>
|
||||
<td data-value="95.83" class="pct high">95.83%</td>
|
||||
<td data-value="24" class="abs high">23/24</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="10" class="abs high">10/10</td>
|
||||
<td data-value="92.3" class="pct high">92.3%</td>
|
||||
<td data-value="13" class="abs high">12/13</td>
|
||||
<td data-value="95.23" class="pct high">95.23%</td>
|
||||
<td data-value="21" class="abs high">20/21</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="UpdateBanner.tsx"><a href="UpdateBanner.tsx.html">UpdateBanner.tsx</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="3" class="abs high">3/3</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="10" class="abs high">10/10</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="1" class="abs high">1/1</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="2" class="abs high">2/2</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="WikilinkSuggestionMenu.tsx"><a href="WikilinkSuggestionMenu.tsx.html">WikilinkSuggestionMenu.tsx</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="4" class="abs low">0/4</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="4" class="abs low">0/4</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="3" class="abs low">0/3</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="4" class="abs low">0/4</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
@@ -356,7 +521,7 @@
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">33.33% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>1/3</span>
|
||||
<span class='fraction'>3/3</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="strong">75% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/4</span>
|
||||
<span class='fraction'>3/4</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/1</span>
|
||||
<span class='fraction'>1/1</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">33.33% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>1/3</span>
|
||||
<span class='fraction'>3/3</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -111,13 +111,14 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<a name='L49'></a><a href='#L49'>49</a>
|
||||
<a name='L50'></a><a href='#L50'>50</a></td><td class="line-coverage quiet"><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-yes">1x</span>
|
||||
<span class="cline-any cline-yes">9x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -146,9 +147,10 @@
|
||||
<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-yes">102x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">102x</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>
|
||||
@@ -187,16 +189,16 @@ const badgeVariants = cva(
|
||||
}
|
||||
)
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >Badge({</span>
|
||||
function Badge({
|
||||
className,
|
||||
variant = <span class="branch-0 cbranch-no" title="branch not covered" >"default",</span>
|
||||
asChild = <span class="branch-0 cbranch-no" title="branch not covered" >false,</span>
|
||||
variant = "default",
|
||||
asChild = false,
|
||||
...props
|
||||
}: React.ComponentProps<"span"> &
|
||||
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
|
||||
const Comp = <span class="cstat-no" title="statement not covered" >asChild ? Slot.Root : "span"</span>
|
||||
const Comp = asChild ? <span class="branch-0 cbranch-no" title="branch not covered" >Slot.Root : "</span>span"
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
return (
|
||||
<Comp
|
||||
data-slot="badge"
|
||||
data-variant={variant}
|
||||
@@ -206,6 +208,7 @@ function <span class="fstat-no" title="function not covered" >Badge({</span>
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components -- shadcn/ui pattern
|
||||
export { Badge, badgeVariants }
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
@@ -214,7 +217,7 @@ export { Badge, badgeVariants }
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -127,13 +127,14 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<a name='L65'></a><a href='#L65'>65</a>
|
||||
<a name='L66'></a><a href='#L66'>66</a></td><td class="line-coverage quiet"><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-yes">4x</span>
|
||||
<span class="cline-any cline-yes">9x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -177,9 +178,10 @@
|
||||
<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">22x</span>
|
||||
<span class="cline-any cline-yes">284x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">284x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -254,6 +256,7 @@ function Button({
|
||||
)
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-refresh/only-export-components -- shadcn/ui pattern
|
||||
export { Button, buttonVariants }
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
@@ -262,7 +265,7 @@ export { Button, buttonVariants }
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -1,361 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for src/components/ui/card.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/components/ui</a> card.tsx</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/7</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/0</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/7</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/7</span>
|
||||
</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></td><td class="line-coverage quiet"><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-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-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-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-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-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-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>
|
||||
<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></td><td class="text"><pre class="prettyprint lang-js">import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >Card({</span> className, ...props }: React.ComponentProps<"div">) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<div
|
||||
data-slot="card"
|
||||
className={cn(
|
||||
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >CardHeader({</span> className, ...props }: React.ComponentProps<"div">) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<div
|
||||
data-slot="card-header"
|
||||
className={cn(
|
||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >CardTitle({</span> className, ...props }: React.ComponentProps<"div">) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<div
|
||||
data-slot="card-title"
|
||||
className={cn("leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >CardDescription({</span> className, ...props }: React.ComponentProps<"div">) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<div
|
||||
data-slot="card-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >CardAction({</span> className, ...props }: React.ComponentProps<"div">) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<div
|
||||
data-slot="card-action"
|
||||
className={cn(
|
||||
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >CardContent({</span> className, ...props }: React.ComponentProps<"div">) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<div
|
||||
data-slot="card-content"
|
||||
className={cn("px-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >CardFooter({</span> className, ...props }: React.ComponentProps<"div">) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<div
|
||||
data-slot="card-footer"
|
||||
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Card,
|
||||
CardHeader,
|
||||
CardFooter,
|
||||
CardTitle,
|
||||
CardAction,
|
||||
CardDescription,
|
||||
CardContent,
|
||||
}
|
||||
</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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
</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>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">70% </span>
|
||||
<span class="strong">80% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>7/10</span>
|
||||
<span class='fraction'>8/10</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">66.66% </span>
|
||||
<span class="strong">83.33% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>4/6</span>
|
||||
<span class='fraction'>5/6</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">70% </span>
|
||||
<span class="strong">80% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>7/10</span>
|
||||
<span class='fraction'>8/10</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">70% </span>
|
||||
<span class="strong">80% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>7/10</span>
|
||||
<span class='fraction'>8/10</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line medium'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -229,7 +229,7 @@
|
||||
<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">78x</span>
|
||||
<span class="cline-any cline-yes">190x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -241,7 +241,7 @@
|
||||
<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">78x</span>
|
||||
<span class="cline-any cline-yes">190x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -254,7 +254,7 @@
|
||||
<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">18x</span>
|
||||
<span class="cline-any cline-yes">108x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -274,7 +274,7 @@
|
||||
<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">78x</span>
|
||||
<span class="cline-any cline-yes">190x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -301,7 +301,7 @@
|
||||
<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">11x</span>
|
||||
<span class="cline-any cline-yes">71x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -318,7 +318,7 @@
|
||||
<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">11x</span>
|
||||
<span class="cline-any cline-yes">67x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -341,7 +341,7 @@
|
||||
<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">11x</span>
|
||||
<span class="cline-any cline-yes">71x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -354,7 +354,7 @@
|
||||
<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-yes">29x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -443,7 +443,7 @@ function DialogContent({
|
||||
>
|
||||
{children}
|
||||
{showCloseButton && (
|
||||
<span class="branch-1 cbranch-no" title="branch not covered" > <DialogPrimitive.Close</span>
|
||||
<DialogPrimitive.Close
|
||||
data-slot="dialog-close"
|
||||
className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
|
||||
>
|
||||
@@ -506,11 +506,11 @@ function DialogTitle({
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DialogDescription({</span>
|
||||
function DialogDescription({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
return (
|
||||
<DialogPrimitive.Description
|
||||
data-slot="dialog-description"
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
@@ -538,7 +538,7 @@ export {
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -1,856 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for src/components/ui/dropdown-menu.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/components/ui</a> dropdown-menu.tsx</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/15</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/2</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/15</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/15</span>
|
||||
</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></td><td class="line-coverage quiet"><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-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-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-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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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-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>
|
||||
<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>
|
||||
<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-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>
|
||||
<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-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>
|
||||
<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>
|
||||
<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></td><td class="text"><pre class="prettyprint lang-js">"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
|
||||
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenu({</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
|
||||
<span class="cstat-no" title="statement not covered" > return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} /></span>
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuPortal({</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuTrigger({</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.Trigger
|
||||
data-slot="dropdown-menu-trigger"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuContent({</span>
|
||||
className,
|
||||
sideOffset = <span class="branch-0 cbranch-no" title="branch not covered" >4,</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.Portal>
|
||||
<DropdownMenuPrimitive.Content
|
||||
data-slot="dropdown-menu-content"
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</DropdownMenuPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuGroup({</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuItem({</span>
|
||||
className,
|
||||
inset,
|
||||
variant = <span class="branch-0 cbranch-no" title="branch not covered" >"default",</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
|
||||
inset?: boolean
|
||||
variant?: "default" | "destructive"
|
||||
}) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.Item
|
||||
data-slot="dropdown-menu-item"
|
||||
data-inset={inset}
|
||||
data-variant={variant}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuCheckboxItem({</span>
|
||||
className,
|
||||
children,
|
||||
checked,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
data-slot="dropdown-menu-checkbox-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
checked={checked}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuRadioGroup({</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.RadioGroup
|
||||
data-slot="dropdown-menu-radio-group"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuRadioItem({</span>
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
data-slot="dropdown-menu-radio-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
|
||||
<DropdownMenuPrimitive.ItemIndicator>
|
||||
<CircleIcon className="size-2 fill-current" />
|
||||
</DropdownMenuPrimitive.ItemIndicator>
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.RadioItem>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuLabel({</span>
|
||||
className,
|
||||
inset,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.Label
|
||||
data-slot="dropdown-menu-label"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuSeparator({</span>
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.Separator
|
||||
data-slot="dropdown-menu-separator"
|
||||
className={cn("bg-border -mx-1 my-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuShortcut({</span>
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"span">) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<span
|
||||
data-slot="dropdown-menu-shortcut"
|
||||
className={cn(
|
||||
"text-muted-foreground ml-auto text-xs tracking-widest",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuSub({</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
|
||||
<span class="cstat-no" title="statement not covered" > return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} /></span>
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuSubTrigger({</span>
|
||||
className,
|
||||
inset,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
|
||||
inset?: boolean
|
||||
}) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
data-slot="dropdown-menu-sub-trigger"
|
||||
data-inset={inset}
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronRightIcon className="ml-auto size-4" />
|
||||
</DropdownMenuPrimitive.SubTrigger>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >DropdownMenuSubContent({</span>
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
data-slot="dropdown-menu-sub-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
DropdownMenu,
|
||||
DropdownMenuPortal,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuSub,
|
||||
DropdownMenuSubTrigger,
|
||||
DropdownMenuSubContent,
|
||||
}
|
||||
</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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
</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>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">19.67% </span>
|
||||
<span class="strong">88.23% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>12/61</span>
|
||||
<span class='fraction'>15/17</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">22.85% </span>
|
||||
<span class="strong">80% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>8/35</span>
|
||||
<span class='fraction'>12/15</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">16.07% </span>
|
||||
<span class="strong">84.61% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>9/56</span>
|
||||
<span class='fraction'>11/13</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">19.67% </span>
|
||||
<span class="strong">88.23% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>12/61</span>
|
||||
<span class='fraction'>15/17</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
@@ -79,18 +79,18 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td class="file low" data-value="badge.tsx"><a href="badge.tsx.html">badge.tsx</a></td>
|
||||
<td data-value="33.33" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 33%"></div><div class="cover-empty" style="width: 67%"></div></div>
|
||||
<td class="file high" data-value="badge.tsx"><a href="badge.tsx.html">badge.tsx</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="33.33" class="pct low">33.33%</td>
|
||||
<td data-value="3" class="abs low">1/3</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="4" class="abs low">0/4</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="1" class="abs low">0/1</td>
|
||||
<td data-value="33.33" class="pct low">33.33%</td>
|
||||
<td data-value="3" class="abs low">1/3</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="3" class="abs high">3/3</td>
|
||||
<td data-value="75" class="pct medium">75%</td>
|
||||
<td data-value="4" class="abs medium">3/4</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="1" class="abs high">1/1</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="3" class="abs high">3/3</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -109,48 +109,18 @@
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="card.tsx"><a href="card.tsx.html">card.tsx</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
<td class="file high" data-value="dialog.tsx"><a href="dialog.tsx.html">dialog.tsx</a></td>
|
||||
<td data-value="80" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 80%"></div><div class="cover-empty" style="width: 20%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="7" class="abs low">0/7</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="0" class="abs high">0/0</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="7" class="abs low">0/7</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="7" class="abs low">0/7</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="dialog.tsx"><a href="dialog.tsx.html">dialog.tsx</a></td>
|
||||
<td data-value="70" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 70%"></div><div class="cover-empty" style="width: 30%"></div></div>
|
||||
</td>
|
||||
<td data-value="70" class="pct medium">70%</td>
|
||||
<td data-value="10" class="abs medium">7/10</td>
|
||||
<td data-value="66.66" class="pct medium">66.66%</td>
|
||||
<td data-value="6" class="abs medium">4/6</td>
|
||||
<td data-value="70" class="pct medium">70%</td>
|
||||
<td data-value="10" class="abs medium">7/10</td>
|
||||
<td data-value="70" class="pct medium">70%</td>
|
||||
<td data-value="10" class="abs medium">7/10</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="dropdown-menu.tsx"><a href="dropdown-menu.tsx.html">dropdown-menu.tsx</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="15" class="abs low">0/15</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="2" class="abs low">0/2</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="15" class="abs low">0/15</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="15" class="abs low">0/15</td>
|
||||
<td data-value="80" class="pct high">80%</td>
|
||||
<td data-value="10" class="abs high">8/10</td>
|
||||
<td data-value="83.33" class="pct high">83.33%</td>
|
||||
<td data-value="6" class="abs high">5/6</td>
|
||||
<td data-value="80" class="pct high">80%</td>
|
||||
<td data-value="10" class="abs high">8/10</td>
|
||||
<td data-value="80" class="pct high">80%</td>
|
||||
<td data-value="10" class="abs high">8/10</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -168,81 +138,6 @@
|
||||
<td data-value="1" class="abs high">1/1</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="scroll-area.tsx"><a href="scroll-area.tsx.html">scroll-area.tsx</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="2" class="abs low">0/2</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="5" class="abs low">0/5</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="2" class="abs low">0/2</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="2" class="abs low">0/2</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="select.tsx"><a href="select.tsx.html">select.tsx</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="10" class="abs low">0/10</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="7" class="abs low">0/7</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="10" class="abs low">0/10</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="10" class="abs low">0/10</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="separator.tsx"><a href="separator.tsx.html">separator.tsx</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="1" class="abs low">0/1</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="2" class="abs low">0/2</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="1" class="abs low">0/1</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="1" class="abs low">0/1</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="tabs.tsx"><a href="tabs.tsx.html">tabs.tsx</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="5" class="abs low">0/5</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="2" class="abs low">0/2</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="4" class="abs low">0/4</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="5" class="abs low">0/5</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="tooltip.tsx"><a href="tooltip.tsx.html">tooltip.tsx</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="4" class="abs low">0/4</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="2" class="abs low">0/2</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="4" class="abs low">0/4</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="4" class="abs low">0/4</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -251,7 +146,7 @@
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
<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">14x</span>
|
||||
<span class="cline-any cline-yes">84x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -133,7 +133,7 @@ export { Input }
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for src/components/ui/scroll-area.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/components/ui</a> scroll-area.tsx</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/2</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/5</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/2</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/2</span>
|
||||
</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></td><td class="line-coverage quiet"><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>
|
||||
<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>
|
||||
<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></td><td class="text"><pre class="prettyprint lang-js">import * as React from "react"
|
||||
import { ScrollArea as ScrollAreaPrimitive } from "radix-ui"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >ScrollArea({</span>
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<ScrollAreaPrimitive.Root
|
||||
data-slot="scroll-area"
|
||||
className={cn("relative", className)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport
|
||||
data-slot="scroll-area-viewport"
|
||||
className="focus-visible:ring-ring/50 size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1"
|
||||
>
|
||||
{children}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
<ScrollBar />
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >ScrollBar({</span>
|
||||
className,
|
||||
orientation = <span class="branch-0 cbranch-no" title="branch not covered" >"vertical",</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<ScrollAreaPrimitive.ScrollAreaScrollbar
|
||||
data-slot="scroll-area-scrollbar"
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"flex touch-none p-px transition-colors select-none",
|
||||
orientation === "vertical" &&
|
||||
"h-full w-2.5 border-l border-l-transparent",
|
||||
orientation === "horizontal" &&
|
||||
"h-2.5 flex-col border-t border-t-transparent",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ScrollAreaPrimitive.ScrollAreaThumb
|
||||
data-slot="scroll-area-thumb"
|
||||
className="bg-border relative flex-1 rounded-full"
|
||||
/>
|
||||
</ScrollAreaPrimitive.ScrollAreaScrollbar>
|
||||
)
|
||||
}
|
||||
|
||||
export { ScrollArea, ScrollBar }
|
||||
</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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
</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>
|
||||
|
||||
@@ -1,649 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for src/components/ui/select.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/components/ui</a> select.tsx</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/10</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/7</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/10</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/10</span>
|
||||
</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></td><td class="line-coverage quiet"><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-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-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-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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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-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>
|
||||
<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>
|
||||
<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></td><td class="text"><pre class="prettyprint lang-js">import * as React from "react"
|
||||
import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
|
||||
import { Select as SelectPrimitive } from "radix-ui"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >Select({</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
|
||||
<span class="cstat-no" title="statement not covered" > return <SelectPrimitive.Root data-slot="select" {...props} /></span>
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >SelectGroup({</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
|
||||
<span class="cstat-no" title="statement not covered" > return <SelectPrimitive.Group data-slot="select-group" {...props} /></span>
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >SelectValue({</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
|
||||
<span class="cstat-no" title="statement not covered" > return <SelectPrimitive.Value data-slot="select-value" {...props} /></span>
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >SelectTrigger({</span>
|
||||
className,
|
||||
size = <span class="branch-0 cbranch-no" title="branch not covered" >"default",</span>
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
|
||||
size?: "sm" | "default"
|
||||
}) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<SelectPrimitive.Trigger
|
||||
data-slot="select-trigger"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<ChevronDownIcon className="size-4 opacity-50" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >SelectContent({</span>
|
||||
className,
|
||||
children,
|
||||
position = <span class="branch-0 cbranch-no" title="branch not covered" >"item-aligned",</span>
|
||||
align = <span class="branch-0 cbranch-no" title="branch not covered" >"center",</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
data-slot="select-content"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
||||
position === "popper" &&
|
||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||
className
|
||||
)}
|
||||
position={position}
|
||||
align={align}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
"p-1",
|
||||
position === "popper" &&
|
||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >SelectLabel({</span>
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<SelectPrimitive.Label
|
||||
data-slot="select-label"
|
||||
className={cn("text-muted-foreground px-2 py-1.5 text-xs", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >SelectItem({</span>
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<SelectPrimitive.Item
|
||||
data-slot="select-item"
|
||||
className={cn(
|
||||
"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span
|
||||
data-slot="select-item-indicator"
|
||||
className="absolute right-2 flex size-3.5 items-center justify-center"
|
||||
>
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<CheckIcon className="size-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >SelectSeparator({</span>
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<SelectPrimitive.Separator
|
||||
data-slot="select-separator"
|
||||
className={cn("bg-border pointer-events-none -mx-1 my-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >SelectScrollUpButton({</span>
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
data-slot="select-scroll-up-button"
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUpIcon className="size-4" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >SelectScrollDownButton({</span>
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
data-slot="select-scroll-down-button"
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDownIcon className="size-4" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectGroup,
|
||||
SelectItem,
|
||||
SelectLabel,
|
||||
SelectScrollDownButton,
|
||||
SelectScrollUpButton,
|
||||
SelectSeparator,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
}
|
||||
</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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
</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>
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for src/components/ui/separator.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/components/ui</a> separator.tsx</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/1</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/2</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/1</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/1</span>
|
||||
</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></td><td class="line-coverage quiet"><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>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Separator as SeparatorPrimitive } from "radix-ui"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >Separator({</span>
|
||||
className,
|
||||
orientation = <span class="branch-0 cbranch-no" title="branch not covered" >"horizontal",</span>
|
||||
decorative = <span class="branch-0 cbranch-no" title="branch not covered" >true,</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<SeparatorPrimitive.Root
|
||||
data-slot="separator"
|
||||
decorative={decorative}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Separator }
|
||||
</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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
</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>
|
||||
|
||||
@@ -1,352 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for src/components/ui/tabs.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/components/ui</a> tabs.tsx</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/5</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/2</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/4</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/5</span>
|
||||
</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></td><td class="line-coverage quiet"><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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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></td><td class="text"><pre class="prettyprint lang-js">import * as React from "react"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
import { Tabs as TabsPrimitive } from "radix-ui"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >Tabs({</span>
|
||||
className,
|
||||
orientation = <span class="branch-0 cbranch-no" title="branch not covered" >"horizontal",</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<TabsPrimitive.Root
|
||||
data-slot="tabs"
|
||||
data-orientation={orientation}
|
||||
orientation={orientation}
|
||||
className={cn(
|
||||
"group/tabs flex gap-2 data-[orientation=horizontal]:flex-col",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const <span class="cstat-no" title="statement not covered" >tabsListVariants = cva(</span>
|
||||
"rounded-lg p-[3px] group-data-[orientation=horizontal]/tabs:h-9 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-muted",
|
||||
line: "gap-1 bg-transparent",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >TabsList({</span>
|
||||
className,
|
||||
variant = <span class="branch-0 cbranch-no" title="branch not covered" >"default",</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.List> &
|
||||
VariantProps<typeof tabsListVariants>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<TabsPrimitive.List
|
||||
data-slot="tabs-list"
|
||||
data-variant={variant}
|
||||
className={cn(tabsListVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >TabsTrigger({</span>
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<TabsPrimitive.Trigger
|
||||
data-slot="tabs-trigger"
|
||||
className={cn(
|
||||
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring text-foreground/60 hover:text-foreground dark:text-muted-foreground dark:hover:text-foreground relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-all group-data-[orientation=vertical]/tabs:w-full group-data-[orientation=vertical]/tabs:justify-start focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 group-data-[variant=default]/tabs-list:data-[state=active]:shadow-sm group-data-[variant=line]/tabs-list:data-[state=active]:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:border-transparent dark:group-data-[variant=line]/tabs-list:data-[state=active]:bg-transparent",
|
||||
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 data-[state=active]:text-foreground",
|
||||
"after:bg-foreground after:absolute after:opacity-0 after:transition-opacity group-data-[orientation=horizontal]/tabs:after:inset-x-0 group-data-[orientation=horizontal]/tabs:after:bottom-[-5px] group-data-[orientation=horizontal]/tabs:after:h-0.5 group-data-[orientation=vertical]/tabs:after:inset-y-0 group-data-[orientation=vertical]/tabs:after:-right-1 group-data-[orientation=vertical]/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-[state=active]:after:opacity-100",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >TabsContent({</span>
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<TabsPrimitive.Content
|
||||
data-slot="tabs-content"
|
||||
className={cn("flex-1 outline-none", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }
|
||||
</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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
</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>
|
||||
|
||||
@@ -1,256 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for src/components/ui/tooltip.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/components/ui</a> tooltip.tsx</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/4</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/2</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/4</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/4</span>
|
||||
</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></td><td class="line-coverage quiet"><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-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-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-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>
|
||||
<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></td><td class="text"><pre class="prettyprint lang-js">"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Tooltip as TooltipPrimitive } from "radix-ui"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >TooltipProvider({</span>
|
||||
delayDuration = <span class="branch-0 cbranch-no" title="branch not covered" >0,</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<TooltipPrimitive.Provider
|
||||
data-slot="tooltip-provider"
|
||||
delayDuration={delayDuration}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >Tooltip({</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
|
||||
<span class="cstat-no" title="statement not covered" > return <TooltipPrimitive.Root data-slot="tooltip" {...props} /></span>
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >TooltipTrigger({</span>
|
||||
...props
|
||||
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
||||
<span class="cstat-no" title="statement not covered" > return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} /></span>
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >TooltipContent({</span>
|
||||
className,
|
||||
sideOffset = <span class="branch-0 cbranch-no" title="branch not covered" >0,</span>
|
||||
children,
|
||||
...props
|
||||
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
|
||||
<span class="cstat-no" title="statement not covered" > return (</span>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipPrimitive.Content
|
||||
data-slot="tooltip-content"
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<TooltipPrimitive.Arrow className="bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" />
|
||||
</TooltipPrimitive.Content>
|
||||
</TooltipPrimitive.Portal>
|
||||
)
|
||||
}
|
||||
|
||||
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
|
||||
</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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
</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>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">27.51% </span>
|
||||
<span class="strong">85.11% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>153/556</span>
|
||||
<span class='fraction'>915/1075</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">16.45% </span>
|
||||
<span class="strong">78.01% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>38/231</span>
|
||||
<span class='fraction'>323/414</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">26.01% </span>
|
||||
<span class="strong">77.91% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>32/123</span>
|
||||
<span class='fraction'>254/326</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">29.77% </span>
|
||||
<span class="strong">87.99% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>145/487</span>
|
||||
<span class='fraction'>762/866</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
@@ -79,78 +79,243 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td class="file low" data-value="useAIChat.ts"><a href="useAIChat.ts.html">useAIChat.ts</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
<td class="file high" data-value="mockFrontmatterHelpers.ts"><a href="mockFrontmatterHelpers.ts.html">mockFrontmatterHelpers.ts</a></td>
|
||||
<td data-value="98.03" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 98%"></div><div class="cover-empty" style="width: 2%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="74" class="abs low">0/74</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="30" class="abs low">0/30</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="15" class="abs low">0/15</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="61" class="abs low">0/61</td>
|
||||
<td data-value="98.03" class="pct high">98.03%</td>
|
||||
<td data-value="51" class="abs high">50/51</td>
|
||||
<td data-value="96.87" class="pct high">96.87%</td>
|
||||
<td data-value="32" class="abs high">31/32</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="9" class="abs high">9/9</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="42" class="abs high">42/42</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="useAppKeyboard.ts"><a href="useAppKeyboard.ts.html">useAppKeyboard.ts</a></td>
|
||||
<td class="file high" data-value="useAppCommands.ts"><a href="useAppCommands.ts.html">useAppCommands.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="5" class="abs high">5/5</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="0" class="abs high">0/0</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="1" class="abs high">1/1</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="5" class="abs high">5/5</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useAppKeyboard.ts"><a href="useAppKeyboard.ts.html">useAppKeyboard.ts</a></td>
|
||||
<td data-value="95.45" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 95%"></div><div class="cover-empty" style="width: 5%"></div></div>
|
||||
</td>
|
||||
<td data-value="95.45" class="pct high">95.45%</td>
|
||||
<td data-value="44" class="abs high">42/44</td>
|
||||
<td data-value="96.55" class="pct high">96.55%</td>
|
||||
<td data-value="29" class="abs high">28/29</td>
|
||||
<td data-value="84.61" class="pct high">84.61%</td>
|
||||
<td data-value="13" class="abs high">11/13</td>
|
||||
<td data-value="94.44" class="pct high">94.44%</td>
|
||||
<td data-value="36" class="abs high">34/36</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="useCommandRegistry.ts"><a href="useCommandRegistry.ts.html">useCommandRegistry.ts</a></td>
|
||||
<td data-value="46.42" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 46%"></div><div class="cover-empty" style="width: 54%"></div></div>
|
||||
</td>
|
||||
<td data-value="46.42" class="pct low">46.42%</td>
|
||||
<td data-value="28" class="abs low">13/28</td>
|
||||
<td data-value="42.85" class="pct low">42.85%</td>
|
||||
<td data-value="14" class="abs low">6/14</td>
|
||||
<td data-value="36.84" class="pct low">36.84%</td>
|
||||
<td data-value="19" class="abs low">7/19</td>
|
||||
<td data-value="50" class="pct medium">50%</td>
|
||||
<td data-value="24" class="abs medium">12/24</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useCommitFlow.ts"><a href="useCommitFlow.ts.html">useCommitFlow.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="17" class="abs high">17/17</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="0" class="abs high">0/0</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="4" class="abs high">4/4</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="16" class="abs high">16/16</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="useDialogs.ts"><a href="useDialogs.ts.html">useDialogs.ts</a></td>
|
||||
<td data-value="60" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 60%"></div><div class="cover-empty" style="width: 40%"></div></div>
|
||||
</td>
|
||||
<td data-value="60" class="pct medium">60%</td>
|
||||
<td data-value="35" class="abs medium">21/35</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="0" class="abs high">0/0</td>
|
||||
<td data-value="6.66" class="pct low">6.66%</td>
|
||||
<td data-value="15" class="abs low">1/15</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="21" class="abs high">21/21</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useEditorSave.ts"><a href="useEditorSave.ts.html">useEditorSave.ts</a></td>
|
||||
<td data-value="96.77" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 96%"></div><div class="cover-empty" style="width: 4%"></div></div>
|
||||
</td>
|
||||
<td data-value="96.77" class="pct high">96.77%</td>
|
||||
<td data-value="31" class="abs high">30/31</td>
|
||||
<td data-value="90.9" class="pct high">90.9%</td>
|
||||
<td data-value="11" class="abs high">10/11</td>
|
||||
<td data-value="90" class="pct high">90%</td>
|
||||
<td data-value="10" class="abs high">9/10</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="27" class="abs high">27/27</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useEntryActions.ts"><a href="useEntryActions.ts.html">useEntryActions.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="36" class="abs high">36/36</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="6" class="abs high">6/6</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="9" class="abs high">9/9</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="33" class="abs high">33/33</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useGitHistory.ts"><a href="useGitHistory.ts.html">useGitHistory.ts</a></td>
|
||||
<td data-value="83.33" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 83%"></div><div class="cover-empty" style="width: 17%"></div></div>
|
||||
</td>
|
||||
<td data-value="83.33" class="pct high">83.33%</td>
|
||||
<td data-value="6" class="abs high">5/6</td>
|
||||
<td data-value="50" class="pct medium">50%</td>
|
||||
<td data-value="4" class="abs medium">2/4</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="2" class="abs high">2/2</td>
|
||||
<td data-value="80" class="pct high">80%</td>
|
||||
<td data-value="5" class="abs high">4/5</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useImageDrop.ts"><a href="useImageDrop.ts.html">useImageDrop.ts</a></td>
|
||||
<td data-value="95.74" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 95%"></div><div class="cover-empty" style="width: 5%"></div></div>
|
||||
</td>
|
||||
<td data-value="95.74" class="pct high">95.74%</td>
|
||||
<td data-value="47" class="abs high">45/47</td>
|
||||
<td data-value="87.5" class="pct high">87.5%</td>
|
||||
<td data-value="16" class="abs high">14/16</td>
|
||||
<td data-value="90.9" class="pct high">90.9%</td>
|
||||
<td data-value="11" class="abs high">10/11</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="39" class="abs high">39/39</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useKeyboardNavigation.ts"><a href="useKeyboardNavigation.ts.html">useKeyboardNavigation.ts</a></td>
|
||||
<td data-value="94.82" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 94%"></div><div class="cover-empty" style="width: 6%"></div></div>
|
||||
</td>
|
||||
<td data-value="94.82" class="pct high">94.82%</td>
|
||||
<td data-value="58" class="abs high">55/58</td>
|
||||
<td data-value="85.36" class="pct high">85.36%</td>
|
||||
<td data-value="41" class="abs high">35/41</td>
|
||||
<td data-value="93.33" class="pct high">93.33%</td>
|
||||
<td data-value="15" class="abs high">14/15</td>
|
||||
<td data-value="95.74" class="pct high">95.74%</td>
|
||||
<td data-value="47" class="abs high">45/47</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="useMenuEvents.ts"><a href="useMenuEvents.ts.html">useMenuEvents.ts</a></td>
|
||||
<td data-value="75" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 75%"></div><div class="cover-empty" style="width: 25%"></div></div>
|
||||
</td>
|
||||
<td data-value="75" class="pct medium">75%</td>
|
||||
<td data-value="20" class="abs medium">15/20</td>
|
||||
<td data-value="37.5" class="pct low">37.5%</td>
|
||||
<td data-value="8" class="abs low">3/8</td>
|
||||
<td data-value="75" class="pct medium">75%</td>
|
||||
<td data-value="8" class="abs medium">6/8</td>
|
||||
<td data-value="80" class="pct high">80%</td>
|
||||
<td data-value="15" class="abs high">12/15</td>
|
||||
<td data-value="40" class="abs medium">30/40</td>
|
||||
<td data-value="86.66" class="pct high">86.66%</td>
|
||||
<td data-value="15" class="abs high">13/15</td>
|
||||
<td data-value="33.33" class="pct low">33.33%</td>
|
||||
<td data-value="12" class="abs low">4/12</td>
|
||||
<td data-value="73.07" class="pct medium">73.07%</td>
|
||||
<td data-value="26" class="abs medium">19/26</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="useKeyboardNavigation.ts"><a href="useKeyboardNavigation.ts.html">useKeyboardNavigation.ts</a></td>
|
||||
<td data-value="51.85" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 51%"></div><div class="cover-empty" style="width: 49%"></div></div>
|
||||
<td class="file high" data-value="useNavigationHistory.ts"><a href="useNavigationHistory.ts.html">useNavigationHistory.ts</a></td>
|
||||
<td data-value="97.56" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 97%"></div><div class="cover-empty" style="width: 3%"></div></div>
|
||||
</td>
|
||||
<td data-value="51.85" class="pct medium">51.85%</td>
|
||||
<td data-value="54" class="abs medium">28/54</td>
|
||||
<td data-value="26.31" class="pct low">26.31%</td>
|
||||
<td data-value="38" class="abs low">10/38</td>
|
||||
<td data-value="54.54" class="pct medium">54.54%</td>
|
||||
<td data-value="11" class="abs medium">6/11</td>
|
||||
<td data-value="56.25" class="pct medium">56.25%</td>
|
||||
<td data-value="48" class="abs medium">27/48</td>
|
||||
<td data-value="97.56" class="pct high">97.56%</td>
|
||||
<td data-value="41" class="abs high">40/41</td>
|
||||
<td data-value="77.27" class="pct medium">77.27%</td>
|
||||
<td data-value="22" class="abs medium">17/22</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="9" class="abs high">9/9</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="35" class="abs high">35/35</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="useMcpBridge.ts"><a href="useMcpBridge.ts.html">useMcpBridge.ts</a></td>
|
||||
<td data-value="0" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
<td class="file medium" data-value="useNoteActions.ts"><a href="useNoteActions.ts.html">useNoteActions.ts</a></td>
|
||||
<td data-value="76.12" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 76%"></div><div class="cover-empty" style="width: 24%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="48" class="abs low">0/48</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="11" class="abs low">0/11</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="14" class="abs low">0/14</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="47" class="abs low">0/47</td>
|
||||
<td data-value="76.12" class="pct medium">76.12%</td>
|
||||
<td data-value="155" class="abs medium">118/155</td>
|
||||
<td data-value="68.75" class="pct medium">68.75%</td>
|
||||
<td data-value="64" class="abs medium">44/64</td>
|
||||
<td data-value="70" class="pct medium">70%</td>
|
||||
<td data-value="50" class="abs medium">35/50</td>
|
||||
<td data-value="78.29" class="pct medium">78.29%</td>
|
||||
<td data-value="129" class="abs medium">101/129</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="useNoteActions.ts"><a href="useNoteActions.ts.html">useNoteActions.ts</a></td>
|
||||
<td data-value="13.87" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 13%"></div><div class="cover-empty" style="width: 87%"></div></div>
|
||||
<td class="file high" data-value="useNoteSearch.ts"><a href="useNoteSearch.ts.html">useNoteSearch.ts</a></td>
|
||||
<td data-value="96.15" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 96%"></div><div class="cover-empty" style="width: 4%"></div></div>
|
||||
</td>
|
||||
<td data-value="13.87" class="pct low">13.87%</td>
|
||||
<td data-value="245" class="abs low">34/245</td>
|
||||
<td data-value="1.88" class="pct low">1.88%</td>
|
||||
<td data-value="106" class="abs low">2/106</td>
|
||||
<td data-value="8.88" class="pct low">8.88%</td>
|
||||
<td data-value="45" class="abs low">4/45</td>
|
||||
<td data-value="15.23" class="pct low">15.23%</td>
|
||||
<td data-value="210" class="abs low">32/210</td>
|
||||
<td data-value="96.15" class="pct high">96.15%</td>
|
||||
<td data-value="26" class="abs high">25/26</td>
|
||||
<td data-value="89.47" class="pct high">89.47%</td>
|
||||
<td data-value="19" class="abs high">17/19</td>
|
||||
<td data-value="91.66" class="pct high">91.66%</td>
|
||||
<td data-value="12" class="abs high">11/12</td>
|
||||
<td data-value="95.83" class="pct high">95.83%</td>
|
||||
<td data-value="24" class="abs high">23/24</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useSaveNote.ts"><a href="useSaveNote.ts.html">useSaveNote.ts</a></td>
|
||||
<td data-value="88.88" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 88%"></div><div class="cover-empty" style="width: 12%"></div></div>
|
||||
</td>
|
||||
<td data-value="88.88" class="pct high">88.88%</td>
|
||||
<td data-value="9" class="abs high">8/9</td>
|
||||
<td data-value="50" class="pct medium">50%</td>
|
||||
<td data-value="4" class="abs medium">2/4</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="3" class="abs high">3/3</td>
|
||||
<td data-value="88.88" class="pct high">88.88%</td>
|
||||
<td data-value="9" class="abs high">8/9</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
@@ -168,6 +333,36 @@
|
||||
<td data-value="20" class="abs high">20/20</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useSettings.ts"><a href="useSettings.ts.html">useSettings.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="18" class="abs high">18/18</td>
|
||||
<td data-value="75" class="pct medium">75%</td>
|
||||
<td data-value="4" class="abs medium">3/4</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="5" class="abs high">5/5</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="18" class="abs high">18/18</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useTabManagement.ts"><a href="useTabManagement.ts.html">useTabManagement.ts</a></td>
|
||||
<td data-value="84.21" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 84%"></div><div class="cover-empty" style="width: 16%"></div></div>
|
||||
</td>
|
||||
<td data-value="84.21" class="pct high">84.21%</td>
|
||||
<td data-value="114" class="abs high">96/114</td>
|
||||
<td data-value="64.28" class="pct medium">64.28%</td>
|
||||
<td data-value="28" class="abs medium">18/28</td>
|
||||
<td data-value="90.24" class="pct high">90.24%</td>
|
||||
<td data-value="41" class="abs high">37/41</td>
|
||||
<td data-value="84.81" class="pct high">84.81%</td>
|
||||
<td data-value="79" class="abs high">67/79</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useTheme.ts"><a href="useTheme.ts.html">useTheme.ts</a></td>
|
||||
<td data-value="95.45" class="pic high">
|
||||
@@ -184,18 +379,78 @@
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="useVaultLoader.ts"><a href="useVaultLoader.ts.html">useVaultLoader.ts</a></td>
|
||||
<td data-value="47.22" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 47%"></div><div class="cover-empty" style="width: 53%"></div></div>
|
||||
<td class="file high" data-value="useUnifiedSearch.ts"><a href="useUnifiedSearch.ts.html">useUnifiedSearch.ts</a></td>
|
||||
<td data-value="85.33" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 85%"></div><div class="cover-empty" style="width: 15%"></div></div>
|
||||
</td>
|
||||
<td data-value="47.22" class="pct low">47.22%</td>
|
||||
<td data-value="72" class="abs low">34/72</td>
|
||||
<td data-value="18.75" class="pct low">18.75%</td>
|
||||
<td data-value="16" class="abs low">3/16</td>
|
||||
<td data-value="26.31" class="pct low">26.31%</td>
|
||||
<td data-value="19" class="abs low">5/19</td>
|
||||
<td data-value="51.51" class="pct medium">51.51%</td>
|
||||
<td data-value="66" class="abs medium">34/66</td>
|
||||
<td data-value="85.33" class="pct high">85.33%</td>
|
||||
<td data-value="75" class="abs high">64/75</td>
|
||||
<td data-value="60" class="pct medium">60%</td>
|
||||
<td data-value="20" class="abs medium">12/20</td>
|
||||
<td data-value="93.75" class="pct high">93.75%</td>
|
||||
<td data-value="16" class="abs high">15/16</td>
|
||||
<td data-value="88.33" class="pct high">88.33%</td>
|
||||
<td data-value="60" class="abs high">53/60</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useUpdater.ts"><a href="useUpdater.ts.html">useUpdater.ts</a></td>
|
||||
<td data-value="85.41" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 85%"></div><div class="cover-empty" style="width: 15%"></div></div>
|
||||
</td>
|
||||
<td data-value="85.41" class="pct high">85.41%</td>
|
||||
<td data-value="48" class="abs high">41/48</td>
|
||||
<td data-value="81.81" class="pct high">81.81%</td>
|
||||
<td data-value="22" class="abs high">18/22</td>
|
||||
<td data-value="90" class="pct high">90%</td>
|
||||
<td data-value="10" class="abs high">9/10</td>
|
||||
<td data-value="86.04" class="pct high">86.04%</td>
|
||||
<td data-value="43" class="abs high">37/43</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useVaultLoader.ts"><a href="useVaultLoader.ts.html">useVaultLoader.ts</a></td>
|
||||
<td data-value="84.15" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 84%"></div><div class="cover-empty" style="width: 16%"></div></div>
|
||||
</td>
|
||||
<td data-value="84.15" class="pct high">84.15%</td>
|
||||
<td data-value="101" class="abs high">85/101</td>
|
||||
<td data-value="73.07" class="pct medium">73.07%</td>
|
||||
<td data-value="26" class="abs medium">19/26</td>
|
||||
<td data-value="86.48" class="pct high">86.48%</td>
|
||||
<td data-value="37" class="abs high">32/37</td>
|
||||
<td data-value="85.5" class="pct high">85.5%</td>
|
||||
<td data-value="69" class="abs high">59/69</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="useVaultSwitcher.ts"><a href="useVaultSwitcher.ts.html">useVaultSwitcher.ts</a></td>
|
||||
<td data-value="45.45" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 45%"></div><div class="cover-empty" style="width: 55%"></div></div>
|
||||
</td>
|
||||
<td data-value="45.45" class="pct low">45.45%</td>
|
||||
<td data-value="33" class="abs low">15/33</td>
|
||||
<td data-value="12.5" class="pct low">12.5%</td>
|
||||
<td data-value="8" class="abs low">1/8</td>
|
||||
<td data-value="33.33" class="pct low">33.33%</td>
|
||||
<td data-value="9" class="abs low">3/9</td>
|
||||
<td data-value="44.44" class="pct low">44.44%</td>
|
||||
<td data-value="27" class="abs low">12/27</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="useViewMode.ts"><a href="useViewMode.ts.html">useViewMode.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="14" class="abs high">14/14</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="7" class="abs high">7/7</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="3" class="abs high">3/3</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="12" class="abs high">12/12</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
@@ -206,7 +461,7 @@
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -1,409 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for src/hooks/useAIChat.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> useAIChat.ts</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/74</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/30</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/15</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/61</span>
|
||||
</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></td><td class="line-coverage quiet"><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-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<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-neutral"> </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-no"> </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-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-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-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<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-neutral"> </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-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-no"> </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-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<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-neutral"> </span>
|
||||
<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-neutral"> </span>
|
||||
<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-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-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>
|
||||
<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-no"> </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-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">/**
|
||||
* Custom hook encapsulating AI chat state and message handling.
|
||||
*/
|
||||
import { useState, useCallback, useRef } from 'react'
|
||||
import type { VaultEntry } from '../types'
|
||||
import {
|
||||
type ChatMessage, nextMessageId, getApiKey,
|
||||
buildSystemPrompt, streamChat,
|
||||
} from '../utils/ai-chat'
|
||||
import { countWords } from '../utils/wikilinks'
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >generateMockResponse(m</span>essage: string, entry: VaultEntry | null, content: string): string {
|
||||
const title = <span class="cstat-no" title="statement not covered" >entry?.title ?? 'Untitled'</span>
|
||||
const <span class="cstat-no" title="statement not covered" >words = countWords(content)</span>
|
||||
const lower = <span class="cstat-no" title="statement not covered" >message.toLowerCase()</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > if (lower.includes('summarize')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return `This note is about **${title}**. It contains ${words} words covering the main concepts documented in your vault.`</span>
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > if (lower.includes('expand')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return `Here are some ways to expand this note:\n\n1. Add more detail to the introduction\n2. Include related examples\n3. Connect it to your quarterly goals\n4. Add a summary section at the end`</span>
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > if (lower.includes('grammar')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return `I reviewed the document for grammar issues. The writing looks clean overall — no major errors found.`</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return `Based on **${title}**, I can help with analysis, summarization, or expansion. What would you like to focus on?`</span>
|
||||
}
|
||||
|
||||
export function <span class="fstat-no" title="function not covered" >useAIChat(</span>
|
||||
entry: VaultEntry | null,
|
||||
allContent: Record<string, string>,
|
||||
contextNotes: VaultEntry[],
|
||||
model: string,
|
||||
) {
|
||||
const [messages, setMessages<span class="cstat-no" title="statement not covered" >] = useState<ChatMessage[]>([])</span>
|
||||
const [isStreaming, setIsStreaming<span class="cstat-no" title="statement not covered" >] = useState(false)</span>
|
||||
const [streamingContent, setStreamingContent<span class="cstat-no" title="statement not covered" >] = useState('')</span>
|
||||
const <span class="cstat-no" title="statement not covered" >abortRef = useRef(false)</span>
|
||||
|
||||
const <span class="cstat-no" title="statement not covered" >sendMessage = useCallback(<span class="fstat-no" title="function not covered" >(t</span>ext: string) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (!text.trim() || isStreaming) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
|
||||
const userMsg: ChatMessage = <span class="cstat-no" title="statement not covered" >{ role: 'user', content: text.trim(), id: nextMessageId() }</span>
|
||||
<span class="cstat-no" title="statement not covered" > setMessages(<span class="fstat-no" title="function not covered" >prev => <span class="cstat-no" title="statement not covered" >[</span>...prev, userMsg])</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > setIsStreaming(true)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setStreamingContent('')</span>
|
||||
<span class="cstat-no" title="statement not covered" > abortRef.current = false</span>
|
||||
|
||||
const allMessages = <span class="cstat-no" title="statement not covered" >[...messages, userMsg].map(<span class="fstat-no" title="function not covered" >m => (<span class="cstat-no" title="statement not covered" >{</span> role: m.role, content: m.content }))</span></span>
|
||||
const hasApiKey = <span class="cstat-no" title="statement not covered" >!!getApiKey()</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > if (!hasApiKey) {</span>
|
||||
const content = <span class="cstat-no" title="statement not covered" >entry ? (allContent[entry.path] ?? '') : ''</span>
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >() => {</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > if (abortRef.current) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
const response = <span class="cstat-no" title="statement not covered" >generateMockResponse(text, entry, content)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setMessages(<span class="fstat-no" title="function not covered" >prev => <span class="cstat-no" title="statement not covered" >[</span>...prev, { role: 'assistant', content: response, id: nextMessageId() }])</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > setIsStreaming(false)</span>
|
||||
}, 800)
|
||||
<span class="cstat-no" title="statement not covered" > return</span>
|
||||
}
|
||||
|
||||
const { prompt: systemPrompt <span class="cstat-no" title="statement not covered" >} = buildSystemPrompt(contextNotes, allContent, model)</span>
|
||||
let accumulated = <span class="cstat-no" title="statement not covered" >''</span>
|
||||
|
||||
const onChunk = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >(c</span>hunk: string) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (abortRef.current) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > accumulated += chunk</span>
|
||||
<span class="cstat-no" title="statement not covered" > setStreamingContent(accumulated)</span>
|
||||
}
|
||||
|
||||
const onDone = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >() => {</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > if (abortRef.current) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > setMessages(<span class="fstat-no" title="function not covered" >prev => <span class="cstat-no" title="statement not covered" >[</span>...prev, { role: 'assistant', content: accumulated, id: nextMessageId() }])</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > setStreamingContent('')</span>
|
||||
<span class="cstat-no" title="statement not covered" > setIsStreaming(false)</span>
|
||||
}
|
||||
|
||||
const onError = <span class="cstat-no" title="statement not covered" ><span class="fstat-no" title="function not covered" >(e</span>rror: string) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (abortRef.current) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > setMessages(<span class="fstat-no" title="function not covered" >prev => <span class="cstat-no" title="statement not covered" >[</span>...prev, { role: 'assistant', content: `Error: ${error}`, id: nextMessageId() }])</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > setStreamingContent('')</span>
|
||||
<span class="cstat-no" title="statement not covered" > setIsStreaming(false)</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > streamChat(allMessages, systemPrompt, model, onChunk, onDone, onError)</span>
|
||||
}, [isStreaming, entry, allContent, contextNotes, model, messages])
|
||||
|
||||
const <span class="cstat-no" title="statement not covered" >clearConversation = useCallback(<span class="fstat-no" title="function not covered" >() => {</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > abortRef.current = true</span>
|
||||
<span class="cstat-no" title="statement not covered" > setMessages([])</span>
|
||||
<span class="cstat-no" title="statement not covered" > setIsStreaming(false)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setStreamingContent('')</span>
|
||||
}, [])
|
||||
|
||||
const <span class="cstat-no" title="statement not covered" >retryMessage = useCallback(<span class="fstat-no" title="function not covered" >(m</span>sgIndex: number) => {</span>
|
||||
const userMsgIndex = <span class="cstat-no" title="statement not covered" >msgIndex - 1</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (userMsgIndex < 0) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
const userMsg = <span class="cstat-no" title="statement not covered" >messages[userMsgIndex]</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (userMsg.role !== 'user') <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > setMessages(<span class="fstat-no" title="function not covered" >prev => <span class="cstat-no" title="statement not covered" >p</span>rev.slice(0, msgIndex))</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > sendMessage(userMsg.content)</span>
|
||||
}, [messages, sendMessage])
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return { messages, isStreaming, streamingContent, sendMessage, clearConversation, retryMessage }</span>
|
||||
}
|
||||
</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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
</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>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">75% </span>
|
||||
<span class="strong">95.45% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>15/20</span>
|
||||
<span class='fraction'>42/44</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">37.5% </span>
|
||||
<span class="strong">96.55% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>3/8</span>
|
||||
<span class='fraction'>28/29</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">75% </span>
|
||||
<span class="strong">84.61% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>6/8</span>
|
||||
<span class='fraction'>11/13</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">80% </span>
|
||||
<span class="strong">94.44% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>12/15</span>
|
||||
<span class='fraction'>34/36</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line medium'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -110,7 +110,57 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<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></td><td class="line-coverage quiet"><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>
|
||||
@@ -128,81 +178,181 @@
|
||||
<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">88x</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-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</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-yes">17x</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-yes">17x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</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-yes">10x</span>
|
||||
<span class="cline-any cline-yes">10x</span>
|
||||
<span class="cline-any cline-yes">9x</span>
|
||||
<span class="cline-any cline-yes">9x</span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</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-yes">49x</span>
|
||||
<span class="cline-any cline-yes">196x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">49x</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-yes">1x</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-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">49x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">19x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">17x</span>
|
||||
<span class="cline-any cline-yes">10x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">49x</span>
|
||||
<span class="cline-any cline-yes">49x</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-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<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 } from 'react'
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { useEffect } from 'react'
|
||||
import type { ViewMode } from './useViewMode'
|
||||
|
||||
interface KeyboardActions {
|
||||
onQuickOpen: () => void
|
||||
onCommandPalette: () => void
|
||||
onSearch: () => void
|
||||
onCreateNote: () => void
|
||||
onSave: () => void
|
||||
onOpenSettings: () => void
|
||||
onTrashNote: (path: string) => void
|
||||
onArchiveNote: (path: string) => void
|
||||
onSetViewMode: (mode: ViewMode) => void
|
||||
onGoBack?: () => void
|
||||
onGoForward?: () => void
|
||||
activeTabPathRef: React.MutableRefObject<string | null>
|
||||
handleCloseTabRef: React.MutableRefObject<(path: string) => void>
|
||||
}
|
||||
|
||||
type ShortcutHandler = () => void
|
||||
|
||||
const TEXT_EDITING_KEYS = new Set(['Backspace', 'Delete'])
|
||||
|
||||
function isTextInputFocused(): boolean {
|
||||
const tag = document.activeElement?.tagName
|
||||
return tag === 'INPUT' || tag === 'TEXTAREA'
|
||||
}
|
||||
|
||||
const VIEW_MODE_KEYS: Record<string, ViewMode> = {
|
||||
'1': 'editor-only',
|
||||
'2': 'editor-list',
|
||||
'3': 'all',
|
||||
}
|
||||
|
||||
function isCmdOnly(e: KeyboardEvent): boolean {
|
||||
return (e.metaKey || e.ctrlKey) && !e.altKey
|
||||
}
|
||||
|
||||
function handleViewModeKey(e: KeyboardEvent, onSetViewMode: (m: ViewMode) => void): boolean {
|
||||
if (!isCmdOnly(e)) return false
|
||||
const mode = VIEW_MODE_KEYS[e.key]
|
||||
if (!mode) return false
|
||||
e.preventDefault()
|
||||
onSetViewMode(mode)
|
||||
return true
|
||||
}
|
||||
|
||||
function handleCmdKey(e: KeyboardEvent, keyMap: Record<string, ShortcutHandler>): boolean {
|
||||
const mod = e.metaKey || e.ctrlKey
|
||||
if (!mod) return false
|
||||
const handler = keyMap[e.key]
|
||||
if (!handler) return false
|
||||
if (TEXT_EDITING_KEYS.has(e.key) && isTextInputFocused()) return false
|
||||
e.preventDefault()
|
||||
handler()
|
||||
return true
|
||||
}
|
||||
|
||||
export function useAppKeyboard({
|
||||
onQuickOpen, onCreateNote, onSave, onTrashNote, onArchiveNote,
|
||||
activeTabPathRef, handleCloseTabRef,
|
||||
onQuickOpen, onCommandPalette, onSearch, onCreateNote, onSave, onOpenSettings, onTrashNote, onArchiveNote,
|
||||
onSetViewMode, onGoBack, onGoForward, activeTabPathRef, handleCloseTabRef,
|
||||
}: KeyboardActions) {
|
||||
const withActiveTab = (fn: (path: string) => void): ShortcutHandler => <span class="fstat-no" title="function not covered" >() => {</span>
|
||||
const path = <span class="cstat-no" title="statement not covered" >activeTabPathRef.current</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (path) <span class="cstat-no" title="statement not covered" >fn(path)</span></span>
|
||||
}
|
||||
|
||||
const keyMap = useMemo((): Record<string, ShortcutHandler> => ({
|
||||
p: onQuickOpen,
|
||||
n: onCreateNote,
|
||||
s: onSave,
|
||||
e: withActiveTab(onArchiveNote),
|
||||
w: withActiveTab(<span class="fstat-no" title="function not covered" >(p</span>ath) => <span class="cstat-no" title="statement not covered" >handleCloseTabRef.current(path))</span>,
|
||||
Backspace: withActiveTab(onTrashNote),
|
||||
Delete: withActiveTab(onTrashNote),
|
||||
}), [onQuickOpen, onCreateNote, onSave, onTrashNote, onArchiveNote, activeTabPathRef, handleCloseTabRef])
|
||||
|
||||
useEffect(() => {
|
||||
const withActiveTab = (fn: (path: string) => void): ShortcutHandler => () => {
|
||||
const path = activeTabPathRef.current
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (path) fn(path)
|
||||
}
|
||||
|
||||
const cmdKeyMap: Record<string, ShortcutHandler> = {
|
||||
k: onCommandPalette,
|
||||
p: onQuickOpen,
|
||||
n: onCreateNote,
|
||||
s: onSave,
|
||||
',': onOpenSettings,
|
||||
e: withActiveTab(onArchiveNote),
|
||||
w: withActiveTab((path) => handleCloseTabRef.current(path)),
|
||||
Backspace: withActiveTab(onTrashNote),
|
||||
Delete: withActiveTab(onTrashNote),
|
||||
'[': <span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >o</span>nGoBack?.(),</span>
|
||||
']': <span class="fstat-no" title="function not covered" >() => <span class="cstat-no" title="statement not covered" >o</span>nGoForward?.(),</span>
|
||||
}
|
||||
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
const mod = e.metaKey || <span class="branch-1 cbranch-no" title="branch not covered" >e.ctrlKey</span>
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (!mod) <span class="cstat-no" title="statement not covered" >return</span>
|
||||
const handler = keyMap[e.key]
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (handler) {
|
||||
// Cmd+Shift+F: full-text search (distinct from Cmd+F browser find)
|
||||
if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key === 'f') {
|
||||
e.preventDefault()
|
||||
handler()
|
||||
onSearch()
|
||||
return
|
||||
}
|
||||
if (!handleViewModeKey(e, onSetViewMode)) {
|
||||
handleCmdKey(e, cmdKeyMap)
|
||||
}
|
||||
}
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
return () => window.removeEventListener('keydown', handleKeyDown)
|
||||
}, [keyMap])
|
||||
}, [onQuickOpen, onCommandPalette, onSearch, onCreateNote, onSave, onOpenSettings, onTrashNote, onArchiveNote, activeTabPathRef, handleCloseTabRef, onSetViewMode, onGoBack, onGoForward])
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
@@ -211,7 +361,7 @@ export function useAppKeyboard({
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">51.85% </span>
|
||||
<span class="strong">94.82% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>28/54</span>
|
||||
<span class='fraction'>55/58</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">26.31% </span>
|
||||
<span class="strong">85.36% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>10/38</span>
|
||||
<span class='fraction'>35/41</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">54.54% </span>
|
||||
<span class="strong">93.33% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>6/11</span>
|
||||
<span class='fraction'>14/15</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">56.25% </span>
|
||||
<span class="strong">95.74% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>27/48</span>
|
||||
<span class='fraction'>45/47</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line medium'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -186,10 +186,7 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<a name='L124'></a><a href='#L124'>124</a></td><td class="line-coverage quiet"><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>
|
||||
@@ -215,11 +212,11 @@
|
||||
<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">21x</span>
|
||||
<span class="cline-any cline-yes">39x</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">21x</span>
|
||||
<span class="cline-any cline-yes">39x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -228,13 +225,13 @@
|
||||
<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-no"> </span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<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-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>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -244,80 +241,77 @@
|
||||
<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-no"> </span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<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-yes">3x</span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">3x</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-no"> </span>
|
||||
<span class="cline-any cline-no"> </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>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">21x</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-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">1x</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>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<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>
|
||||
<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">7x</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-yes">264x</span>
|
||||
<span class="cline-any cline-yes">264x</span>
|
||||
<span class="cline-any cline-yes">264x</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-yes">44x</span>
|
||||
<span class="cline-any cline-yes">39x</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-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>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<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>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<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>
|
||||
<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'
|
||||
import { filterEntries, sortByModified, buildRelationshipGroups } from '../components/NoteList'
|
||||
import { filterEntries, sortByModified, buildRelationshipGroups } from '../utils/noteListHelpers'
|
||||
import type { VaultEntry, SidebarSelection } from '../types'
|
||||
|
||||
interface Tab {
|
||||
@@ -348,98 +342,95 @@ function computeVisibleNotes(
|
||||
return [...filterEntries(entries, selection)].sort(sortByModified)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >navigateTab(</span>
|
||||
function navigateTab(
|
||||
tabsRef: React.RefObject<Tab[]>,
|
||||
activeTabPathRef: React.RefObject<string | null>,
|
||||
onSwitchTab: React.RefObject<(path: string) => void>,
|
||||
direction: 1 | -1,
|
||||
) {
|
||||
const currentTabs = <span class="cstat-no" title="statement not covered" >tabsRef.current!</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (currentTabs.length === 0) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
const currentTabs = tabsRef.current!
|
||||
if (currentTabs.length === 0) return
|
||||
|
||||
const currentPath = <span class="cstat-no" title="statement not covered" >activeTabPathRef.current</span>
|
||||
const currentIndex = <span class="cstat-no" title="statement not covered" >currentTabs.findIndex(<span class="fstat-no" title="function not covered" >(t</span>) => <span class="cstat-no" title="statement not covered" >t.entry.path === currentPath)</span></span>
|
||||
const <span class="cstat-no" title="statement not covered" >nextIndex = (currentIndex + direction + currentTabs.length) % currentTabs.length</span>
|
||||
<span class="cstat-no" title="statement not covered" > onSwitchTab.current!(currentTabs[nextIndex].entry.path)</span>
|
||||
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)
|
||||
}
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >navigateNote(</span>
|
||||
function navigateNote(
|
||||
visibleNotesRef: React.RefObject<VaultEntry[]>,
|
||||
activeTabPathRef: React.RefObject<string | null>,
|
||||
onReplace: React.RefObject<(entry: VaultEntry) => void>,
|
||||
onSelect: React.RefObject<(entry: VaultEntry) => void>,
|
||||
direction: 1 | -1,
|
||||
) {
|
||||
const notes = <span class="cstat-no" title="statement not covered" >visibleNotesRef.current!</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (notes.length === 0) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
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>
|
||||
|
||||
const currentPath = <span class="cstat-no" title="statement not covered" >activeTabPathRef.current</span>
|
||||
const currentIndex = <span class="cstat-no" title="statement not covered" >notes.findIndex(<span class="fstat-no" title="function not covered" >(n</span>) => <span class="cstat-no" title="statement not covered" >n.path === currentPath)</span></span>
|
||||
const currentPath = activeTabPathRef.current
|
||||
const currentIndex = notes.findIndex((n) => n.path === currentPath)
|
||||
|
||||
const nextIndex = <span class="cstat-no" title="statement not covered" >currentIndex === -1</span>
|
||||
? (direction === 1 ? 0 : notes.length - 1)
|
||||
const nextIndex = currentIndex === -1
|
||||
? (direction === 1 ? 0 : <span class="branch-1 cbranch-no" title="branch not covered" >notes.length - 1)</span>
|
||||
: (currentIndex + direction + notes.length) % notes.length
|
||||
|
||||
const nextNote = <span class="cstat-no" title="statement not covered" >notes[nextIndex]</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (currentPath) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onReplace.current!(nextNote)</span>
|
||||
const nextNote = notes[nextIndex]
|
||||
if (currentPath) {
|
||||
onReplace.current!(nextNote)
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > onSelect.current!(nextNote)</span>
|
||||
onSelect.current!(nextNote)
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
export function useKeyboardNavigation({
|
||||
tabs,
|
||||
activeTabPath,
|
||||
entries,
|
||||
selection,
|
||||
allContent,
|
||||
onSwitchTab,
|
||||
onReplaceActiveTab,
|
||||
onSelectNote,
|
||||
tabs, activeTabPath, entries, selection, allContent,
|
||||
onSwitchTab, onReplaceActiveTab, onSelectNote,
|
||||
}: KeyboardNavigationOptions) {
|
||||
const visibleNotes = useMemo(
|
||||
() => computeVisibleNotes(entries, selection, allContent),
|
||||
[entries, selection, allContent],
|
||||
)
|
||||
|
||||
const tabsRef = useRef(tabs)
|
||||
tabsRef.current = tabs
|
||||
const activeTabPathRef = useRef(activeTabPath)
|
||||
activeTabPathRef.current = activeTabPath
|
||||
const visibleNotesRef = useRef(visibleNotes)
|
||||
visibleNotesRef.current = visibleNotes
|
||||
const onSwitchTabRef = useRef(onSwitchTab)
|
||||
onSwitchTabRef.current = onSwitchTab
|
||||
const onReplaceRef = useRef(onReplaceActiveTab)
|
||||
onReplaceRef.current = onReplaceActiveTab
|
||||
const onSelectNoteRef = useRef(onSelectNote)
|
||||
onSelectNoteRef.current = onSelectNote
|
||||
const tabsRef = useLatestRef(tabs)
|
||||
const activeTabPathRef = useLatestRef(activeTabPath)
|
||||
const visibleNotesRef = useLatestRef(visibleNotes)
|
||||
const onSwitchTabRef = useLatestRef(onSwitchTab)
|
||||
const onReplaceRef = useLatestRef(onReplaceActiveTab)
|
||||
const onSelectNoteRef = useLatestRef(onSelectNote)
|
||||
|
||||
useEffect(() => {
|
||||
const isRunningInTauri = isTauri()
|
||||
|
||||
const inTauri = isTauri()
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
const mod = e.metaKey || <span class="branch-1 cbranch-no" title="branch not covered" >e.ctrlKey</span>
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (!mod) <span class="cstat-no" title="statement not covered" >return</span>
|
||||
|
||||
const isTabShortcut = isRunningInTauri
|
||||
? <span class="branch-0 cbranch-no" title="branch not covered" >e.altKey && !e.shiftKey</span>
|
||||
: e.shiftKey && <span class="branch-1 cbranch-no" title="branch not covered" >!e.altKey</span>
|
||||
const isNoteShortcut = e.altKey && <span class="branch-1 cbranch-no" title="branch not covered" >!e.shiftKey</span>
|
||||
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (isTabShortcut && (<span class="branch-1 cbranch-no" title="branch not covered" >e.key === 'ArrowLeft' || <span class="branch-2 cbranch-no" title="branch not covered" >e</span>.key === 'ArrowRight')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > e.preventDefault()</span>
|
||||
<span class="cstat-no" title="statement not covered" > navigateTab(tabsRef, activeTabPathRef, onSwitchTabRef, e.key === 'ArrowRight' ? 1 : -1)</span>
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>} else if (isNoteShortcut && (<span class="branch-1 cbranch-no" title="branch not covered" >e.key === 'ArrowUp' || <span class="branch-2 cbranch-no" title="branch not covered" >e</span>.key === 'ArrowDown')) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > e.preventDefault()</span>
|
||||
<span class="cstat-no" title="statement not covered" > navigateNote(visibleNotesRef, activeTabPathRef, onReplaceRef, onSelectNoteRef, e.key === 'ArrowDown' ? 1 : -1)</span>
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown)
|
||||
return () => window.removeEventListener('keydown', handleKeyDown)
|
||||
}, [])
|
||||
}, [tabsRef, activeTabPathRef, visibleNotesRef, onSwitchTabRef, onReplaceRef, onSelectNoteRef])
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
|
||||
@@ -448,7 +439,7 @@ export function useKeyboardNavigation({
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -1,424 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for src/hooks/useMcpBridge.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> useMcpBridge.ts</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/48</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/11</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/14</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/47</span>
|
||||
</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></td><td class="line-coverage quiet"><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>
|
||||
<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-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-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<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-neutral"> </span>
|
||||
<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-no"> </span>
|
||||
<span class="cline-any cline-no"> </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-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>
|
||||
<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>
|
||||
<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-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<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-neutral"> </span>
|
||||
<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-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-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-no"> </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-no"> </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-no"> </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-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">/**
|
||||
* Hook for communicating with the Laputa MCP WebSocket bridge.
|
||||
*
|
||||
* Provides typed tool invocations for vault operations:
|
||||
* - readNote, createNote, searchNotes, appendToNote
|
||||
*
|
||||
* Connection is lazy — only opens when first tool is called.
|
||||
*/
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
|
||||
const DEFAULT_WS_URL = <span class="cstat-no" title="statement not covered" >'ws://localhost:9710'</span>
|
||||
|
||||
interface PendingRequest {
|
||||
resolve: (value: any) => void
|
||||
reject: (reason: any) => void
|
||||
}
|
||||
|
||||
interface SearchResult {
|
||||
path: string
|
||||
title: string
|
||||
snippet: string
|
||||
}
|
||||
|
||||
export function <span class="fstat-no" title="function not covered" >useMcpBridge(w</span>sUrl = <span class="branch-0 cbranch-no" title="branch not covered" >DEFAULT_WS_URL) {</span>
|
||||
const <span class="cstat-no" title="statement not covered" >wsRef = useRef<WebSocket | null>(null)</span>
|
||||
const <span class="cstat-no" title="statement not covered" >pendingRef = useRef<Map<string, PendingRequest>>(new Map())</span>
|
||||
const <span class="cstat-no" title="statement not covered" >idCounterRef = useRef(0)</span>
|
||||
const [connected, setConnected<span class="cstat-no" title="statement not covered" >] = useState(false)</span>
|
||||
|
||||
const <span class="cstat-no" title="statement not covered" >ensureConnection = useCallback(<span class="fstat-no" title="function not covered" >(): Promise<WebSocket> => {</span></span>
|
||||
const ws = <span class="cstat-no" title="statement not covered" >wsRef.current</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (ws && ws.readyState === WebSocket.OPEN) <span class="cstat-no" title="statement not covered" >return Promise.resolve(ws)</span></span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return new Promise(<span class="fstat-no" title="function not covered" >(r</span>esolve, reject) => {</span>
|
||||
const newWs = <span class="cstat-no" title="statement not covered" >new WebSocket(wsUrl)</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > newWs.onopen = <span class="fstat-no" title="function not covered" >() => {</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > wsRef.current = newWs</span>
|
||||
<span class="cstat-no" title="statement not covered" > setConnected(true)</span>
|
||||
<span class="cstat-no" title="statement not covered" > resolve(newWs)</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > newWs.onmessage = <span class="fstat-no" title="function not covered" >(e</span>vent) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
const msg = <span class="cstat-no" title="statement not covered" >JSON.parse(event.data)</span>
|
||||
const pending = <span class="cstat-no" title="statement not covered" >pendingRef.current.get(msg.id)</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (pending) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > pendingRef.current.delete(msg.id)</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (msg.error) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > pending.reject(new Error(msg.error))</span>
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > pending.resolve(msg.result)</span>
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore malformed messages
|
||||
}
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > newWs.onclose = <span class="fstat-no" title="function not covered" >() => {</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > wsRef.current = null</span>
|
||||
<span class="cstat-no" title="statement not covered" > setConnected(false)</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > newWs.onerror = <span class="fstat-no" title="function not covered" >() => {</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > reject(new Error('WebSocket connection failed'))</span>
|
||||
}
|
||||
})
|
||||
}, [wsUrl])
|
||||
|
||||
const <span class="cstat-no" title="statement not covered" >callTool = useCallback(<span class="fstat-no" title="function not covered" >async <T>(t</span>ool: string, args: Record<string, any>): Promise<T> => {</span>
|
||||
const ws = <span class="cstat-no" title="statement not covered" >await ensureConnection()</span>
|
||||
const id = <span class="cstat-no" title="statement not covered" >`mcp-${++idCounterRef.current}`</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return new Promise(<span class="fstat-no" title="function not covered" >(r</span>esolve, reject) => {</span>
|
||||
<span class="cstat-no" title="statement not covered" > pendingRef.current.set(id, { resolve, reject })</span>
|
||||
<span class="cstat-no" title="statement not covered" > ws.send(JSON.stringify({ id, tool, args }))</span>
|
||||
|
||||
// Timeout after 30 seconds
|
||||
<span class="cstat-no" title="statement not covered" > setTimeout(<span class="fstat-no" title="function not covered" >() => {</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > if (pendingRef.current.has(id)) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > pendingRef.current.delete(id)</span>
|
||||
<span class="cstat-no" title="statement not covered" > reject(new Error('MCP tool call timed out'))</span>
|
||||
}
|
||||
}, 30_000)
|
||||
})
|
||||
}, [ensureConnection])
|
||||
|
||||
const <span class="cstat-no" title="statement not covered" >readNote = useCallback(</span>
|
||||
<span class="fstat-no" title="function not covered" > (p</span>ath: string) => <span class="cstat-no" title="statement not covered" >callTool<{ content: string }>('read_note', { path }),</span>
|
||||
[callTool],
|
||||
)
|
||||
|
||||
const <span class="cstat-no" title="statement not covered" >createNote = useCallback(</span>
|
||||
<span class="fstat-no" title="function not covered" > (p</span>ath: string, title: string, isA?: string) =>
|
||||
<span class="cstat-no" title="statement not covered" > callTool<string>('create_note', { path, title, is_a: isA }),</span>
|
||||
[callTool],
|
||||
)
|
||||
|
||||
const <span class="cstat-no" title="statement not covered" >searchNotes = useCallback(</span>
|
||||
<span class="fstat-no" title="function not covered" > (q</span>uery: string, limit?: number) =>
|
||||
<span class="cstat-no" title="statement not covered" > callTool<SearchResult[]>('search_notes', { query, limit }),</span>
|
||||
[callTool],
|
||||
)
|
||||
|
||||
const <span class="cstat-no" title="statement not covered" >appendToNote = useCallback(</span>
|
||||
<span class="fstat-no" title="function not covered" > (p</span>ath: string, text: string) =>
|
||||
<span class="cstat-no" title="statement not covered" > callTool<{ ok: boolean }>('append_to_note', { path, text }),</span>
|
||||
[callTool],
|
||||
)
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > return { connected, readNote, createNote, searchNotes, appendToNote, callTool }</span>
|
||||
}
|
||||
</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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
</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>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -112,16 +112,16 @@
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">37x</span>
|
||||
<span class="cline-any cline-yes">37x</span>
|
||||
<span class="cline-any cline-yes">37x</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">2x</span>
|
||||
<span class="cline-any cline-yes">2x</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-yes">35x</span>
|
||||
<span class="cline-any cline-yes">42x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -129,9 +129,9 @@
|
||||
<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">74x</span>
|
||||
<span class="cline-any cline-yes">87x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">74x</span>
|
||||
<span class="cline-any cline-yes">87x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
@@ -144,12 +144,12 @@
|
||||
<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">74x</span>
|
||||
<span class="cline-any cline-yes">592x</span>
|
||||
<span class="cline-any cline-yes">87x</span>
|
||||
<span class="cline-any cline-yes">624x</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-yes">74x</span>
|
||||
<span class="cline-any cline-yes">87x</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 { useState, useCallback } from 'react'
|
||||
|
||||
@@ -202,7 +202,7 @@ export function useSectionVisibility() {
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -124,46 +124,46 @@
|
||||
<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">168x</span>
|
||||
<span class="cline-any cline-yes">462x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">168x</span>
|
||||
<span class="cline-any cline-yes">936x</span>
|
||||
<span class="cline-any cline-yes">462x</span>
|
||||
<span class="cline-any cline-yes">2574x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">936x</span>
|
||||
<span class="cline-any cline-yes">936x</span>
|
||||
<span class="cline-any cline-yes">2574x</span>
|
||||
<span class="cline-any cline-yes">2574x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">928x</span>
|
||||
<span class="cline-any cline-yes">160x</span>
|
||||
<span class="cline-any cline-yes">768x</span>
|
||||
<span class="cline-any cline-yes">2552x</span>
|
||||
<span class="cline-any cline-yes">440x</span>
|
||||
<span class="cline-any cline-yes">2112x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">456x</span>
|
||||
<span class="cline-any cline-yes">1254x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">456x</span>
|
||||
<span class="cline-any cline-yes">456x</span>
|
||||
<span class="cline-any cline-yes">1254x</span>
|
||||
<span class="cline-any cline-yes">1254x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">312x</span>
|
||||
<span class="cline-any cline-yes">858x</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-yes">168x</span>
|
||||
<span class="cline-any cline-yes">462x</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-yes">936x</span>
|
||||
<span class="cline-any cline-yes">2574x</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-yes">8x</span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-yes">768x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">2112x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-yes">22x</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 { useMemo } from 'react'
|
||||
import themeConfig from '../theme.json'
|
||||
@@ -223,7 +223,7 @@ export function useEditorTheme() {
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">47.22% </span>
|
||||
<span class="strong">84.15% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>34/72</span>
|
||||
<span class='fraction'>85/101</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">18.75% </span>
|
||||
<span class="strong">73.07% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>3/16</span>
|
||||
<span class='fraction'>19/26</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">26.31% </span>
|
||||
<span class="strong">86.48% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>5/19</span>
|
||||
<span class='fraction'>32/37</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">51.51% </span>
|
||||
<span class="strong">85.5% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>34/66</span>
|
||||
<span class='fraction'>59/69</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -190,125 +190,30 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<a name='L128'></a><a href='#L128'>128</a></td><td class="line-coverage quiet"><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-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</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-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<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-yes">66x</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-yes">22x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</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>
|
||||
<span class="cline-any cline-yes">22x</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-yes">22x</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-yes">22x</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-no"> </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-no"> </span>
|
||||
<span class="cline-any cline-yes">30x</span>
|
||||
<span class="cline-any cline-yes">30x</span>
|
||||
<span class="cline-any cline-yes">30x</span>
|
||||
<span class="cline-any cline-yes">30x</span>
|
||||
<span class="cline-any cline-yes">30x</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-yes">22x</span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">22x</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-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-yes">22x</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-yes">22x</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>
|
||||
@@ -316,22 +221,97 @@
|
||||
<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-no"> </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-yes">104x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</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-yes">40x</span>
|
||||
<span class="cline-any cline-yes">36x</span>
|
||||
<span class="cline-any cline-yes">36x</span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-yes">1x</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-yes">104x</span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">30x</span>
|
||||
<span class="cline-any cline-yes">30x</span>
|
||||
<span class="cline-any cline-yes">30x</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-yes">104x</span>
|
||||
<span class="cline-any cline-yes">32x</span>
|
||||
<span class="cline-any cline-yes">32x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">22x</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>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">4x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</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>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">33x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">104x</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>
|
||||
@@ -340,138 +320,128 @@
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">import { useCallback, useEffect, useState } from 'react'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { isTauri, mockInvoke } from '../mock-tauri'
|
||||
import type { VaultEntry, GitCommit, ModifiedFile } from '../types'
|
||||
import type { VaultEntry, GitCommit, ModifiedFile, NoteStatus } from '../types'
|
||||
|
||||
function tauriCall<T>(command: string, tauriArgs: Record<string, unknown>, mockArgs?: Record<string, unknown>): Promise<T> {
|
||||
return isTauri(<span class="branch-0 cbranch-no" title="branch not covered" >) ? invoke<T>(command, tauriArgs) : m</span>ockInvoke<T>(command, mockArgs ?? tauriArgs)
|
||||
}
|
||||
|
||||
async function loadVaultData(vaultPath: string) {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (!isTauri()) console.info('[mock] Using mock Tauri data for browser testing')
|
||||
const entries = await tauriCall<VaultEntry[]>('list_vault', { path: vaultPath })
|
||||
console.log(`Vault scan complete: ${entries.length} entries found`)
|
||||
const allContent = isTauri() ? <span class="branch-0 cbranch-no" title="branch not covered" >{} : a</span>wait mockInvoke<Record<string, string>>('get_all_content', { path: vaultPath })
|
||||
return { entries, allContent }
|
||||
}
|
||||
|
||||
async function commitWithPush(vaultPath: string, message: string): Promise<string> {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (!isTauri()) {
|
||||
await mockInvoke<string>('git_commit', { message })
|
||||
await mockInvoke<string>('git_push', {})
|
||||
return 'Committed and pushed'
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > await invoke<string>('git_commit', { vaultPath, message })</span>
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
<span class="cstat-no" title="statement not covered" > await invoke<string>('git_push', { vaultPath })</span>
|
||||
<span class="cstat-no" title="statement not covered" > return 'Committed and pushed'</span>
|
||||
} catch {
|
||||
<span class="cstat-no" title="statement not covered" > return 'Committed (push failed)'</span>
|
||||
}
|
||||
}
|
||||
|
||||
function useNewNoteTracker() {
|
||||
const [newPaths, setNewPaths] = useState<Set<string>>(new Set())
|
||||
|
||||
const trackNew = useCallback((path: string) => {
|
||||
setNewPaths((prev) => new Set(prev).add(path))
|
||||
}, [])
|
||||
|
||||
const clear = useCallback(() => setNewPaths(new Set()), [])
|
||||
|
||||
return { newPaths, trackNew, clear }
|
||||
}
|
||||
|
||||
export function resolveNoteStatus(path: string, newPaths: Set<string>, modifiedFiles: ModifiedFile[]): NoteStatus {
|
||||
if (newPaths.has(path)) return 'new'
|
||||
const gitEntry = modifiedFiles.find((f) => f.path === path)
|
||||
if (!gitEntry) return 'clean'
|
||||
if (gitEntry.status === 'untracked' || gitEntry.status === 'added') return 'new'
|
||||
if (gitEntry.status === 'modified') return 'modified'
|
||||
return 'clean'
|
||||
}
|
||||
|
||||
export function useVaultLoader(vaultPath: string) {
|
||||
const [entries, setEntries] = useState<VaultEntry[]>([])
|
||||
const [allContent, setAllContent] = useState<Record<string, string>>({})
|
||||
const [modifiedFiles, setModifiedFiles] = useState<ModifiedFile[]>([])
|
||||
const tracker = useNewNoteTracker()
|
||||
|
||||
useEffect(() => {
|
||||
setEntries([])
|
||||
setAllContent({})
|
||||
setModifiedFiles([])
|
||||
|
||||
const loadVault = async () => {
|
||||
try {
|
||||
let result: VaultEntry[]
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (isTauri()) {
|
||||
<span class="cstat-no" title="statement not covered" > result = await invoke<VaultEntry[]>('list_vault', { path: vaultPath })</span>
|
||||
} else {
|
||||
console.info('[mock] Using mock Tauri data for browser testing')
|
||||
result = await mockInvoke<VaultEntry[]>('list_vault', { path: vaultPath })
|
||||
}
|
||||
console.log(`Vault scan complete: ${result.length} entries found`)
|
||||
setEntries(result)
|
||||
|
||||
let content: Record<string, string>
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (isTauri()) {
|
||||
<span class="cstat-no" title="statement not covered" > content = {}</span>
|
||||
} else {
|
||||
content = await mockInvoke<Record<string, string>>('get_all_content', { path: vaultPath })
|
||||
}
|
||||
setAllContent(content)
|
||||
} catch (err) {
|
||||
<span class="cstat-no" title="statement not covered" > console.warn('Vault scan failed:', err)</span>
|
||||
}
|
||||
}
|
||||
loadVault()
|
||||
}, [vaultPath])
|
||||
// eslint-disable-next-line react-hooks/set-state-in-effect -- clear stale data then load new vault
|
||||
setEntries([]); setAllContent({}); setModifiedFiles([]); tracker.clear()
|
||||
loadVaultData(vaultPath)
|
||||
.then(({ entries: e, allContent: c }) => { setEntries(e); setAllContent(c) })
|
||||
.catch(<span class="fstat-no" title="function not covered" >(e</span>rr) => <span class="cstat-no" title="statement not covered" >console.warn('Vault scan failed:', err))</span>
|
||||
}, [vaultPath]) // eslint-disable-line react-hooks/exhaustive-deps -- tracker.clear is stable
|
||||
|
||||
const loadModifiedFiles = useCallback(async () => {
|
||||
try {
|
||||
let files: ModifiedFile[]
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (isTauri()) {
|
||||
<span class="cstat-no" title="statement not covered" > files = await invoke<ModifiedFile[]>('get_modified_files', { vaultPath })</span>
|
||||
} else {
|
||||
files = await mockInvoke<ModifiedFile[]>('get_modified_files', {})
|
||||
}
|
||||
setModifiedFiles(files)
|
||||
setModifiedFiles(await tauriCall<ModifiedFile[]>('get_modified_files', { vaultPath }, {}))
|
||||
} catch (err) {
|
||||
<span class="cstat-no" title="statement not covered" > console.warn('Failed to load modified files:', err)</span>
|
||||
<span class="cstat-no" title="statement not covered" > setModifiedFiles([])</span>
|
||||
}
|
||||
}, [vaultPath])
|
||||
|
||||
useEffect(() => {
|
||||
loadModifiedFiles()
|
||||
}, [loadModifiedFiles])
|
||||
useEffect(() => { loadModifiedFiles() }, [loadModifiedFiles]) // eslint-disable-line react-hooks/set-state-in-effect -- trigger initial load
|
||||
|
||||
const addEntry = useCallback(<span class="fstat-no" title="function not covered" >(e</span>ntry: VaultEntry, content: string) => {
|
||||
<span class="cstat-no" title="statement not covered" > setEntries(<span class="fstat-no" title="function not covered" >(p</span>rev) => <span class="cstat-no" title="statement not covered" >[entry, ...prev])</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > setAllContent(<span class="fstat-no" title="function not covered" >(p</span>rev) => (<span class="cstat-no" title="statement not covered" >{ ...prev, [entry.path]: content }))</span></span>
|
||||
const addEntry = useCallback((entry: VaultEntry, content: string) => {
|
||||
setEntries((prev) => {
|
||||
if (prev.some(e => e.path === entry.path)) return prev
|
||||
return [entry, ...prev]
|
||||
})
|
||||
setAllContent((prev) => ({ ...prev, [entry.path]: content }))
|
||||
tracker.trackNew(entry.path)
|
||||
}, [tracker])
|
||||
|
||||
const updateContent = useCallback((path: string, content: string) =>
|
||||
setAllContent((prev) => ({ ...prev, [path]: content })), [])
|
||||
|
||||
const updateEntry = useCallback((path: string, patch: Partial<VaultEntry>) =>
|
||||
setEntries((prev) => prev.map((e) => e.path === path ? { ...e, ...patch } : <span class="branch-1 cbranch-no" title="branch not covered" >e)</span>), [])
|
||||
|
||||
const removeEntry = useCallback((path: string) => {
|
||||
setEntries((prev) => prev.filter((e) => e.path !== path))
|
||||
setAllContent((prev) => { const next = { ...prev }; delete next[path]; return next })
|
||||
}, [])
|
||||
|
||||
const updateContent = useCallback(<span class="fstat-no" title="function not covered" >(p</span>ath: string, content: string) => {
|
||||
<span class="cstat-no" title="statement not covered" > setAllContent(<span class="fstat-no" title="function not covered" >(p</span>rev) => (<span class="cstat-no" title="statement not covered" >{ ...prev, [path]: content }))</span></span>
|
||||
const replaceEntry = useCallback(<span class="fstat-no" title="function not covered" >(o</span>ldPath: string, patch: Partial<VaultEntry> & { path: string }, newContent: string) => {
|
||||
<span class="cstat-no" title="statement not covered" > setEntries(<span class="fstat-no" title="function not covered" >(p</span>rev) => <span class="cstat-no" title="statement not covered" >prev.map(<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >e.path === oldPath ? { ...e, ...patch } : e)</span>)</span></span>
|
||||
<span class="cstat-no" title="statement not covered" > setAllContent(<span class="fstat-no" title="function not covered" >(p</span>rev) => { const next = <span class="cstat-no" title="statement not covered" >{ ...prev }; <span class="cstat-no" title="statement not covered" >d</span>elete next[oldPath]; <span class="cstat-no" title="statement not covered" >n</span>ext[patch.path] = newContent; <span class="cstat-no" title="statement not covered" >r</span>eturn next }</span>)</span>
|
||||
}, [])
|
||||
|
||||
const updateEntry = useCallback(<span class="fstat-no" title="function not covered" >(p</span>ath: string, patch: Partial<VaultEntry>) => {
|
||||
<span class="cstat-no" title="statement not covered" > setEntries(<span class="fstat-no" title="function not covered" >(p</span>rev) => <span class="cstat-no" title="statement not covered" >prev.map(<span class="fstat-no" title="function not covered" >(e</span>) => <span class="cstat-no" title="statement not covered" >e.path === path ? { ...e, ...patch } : e)</span>)</span></span>
|
||||
}, [])
|
||||
|
||||
const loadGitHistory = useCallback(<span class="fstat-no" title="function not covered" >async (p</span>ath: string): Promise<GitCommit[]> => {
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (isTauri()) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return await invoke<GitCommit[]>('get_file_history', { vaultPath, path })</span>
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > return await mockInvoke<GitCommit[]>('get_file_history', { path })</span>
|
||||
}
|
||||
} catch (err) {
|
||||
<span class="cstat-no" title="statement not covered" > console.warn('Failed to load git history:', err)</span>
|
||||
<span class="cstat-no" title="statement not covered" > return []</span>
|
||||
}
|
||||
const loadGitHistory = useCallback(async (path: string): Promise<GitCommit[]> => {
|
||||
try { return await tauriCall<GitCommit[]>('get_file_history', { vaultPath, path }, { path }) }
|
||||
catch (err) { console.warn('Failed to load git history:', err); return [] }
|
||||
}, [vaultPath])
|
||||
|
||||
const loadDiffAtCommit = useCallback(<span class="fstat-no" title="function not covered" >async (p</span>ath: string, commitHash: string): Promise<string> => {
|
||||
<span class="cstat-no" title="statement not covered" > if (isTauri()) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return invoke<string>('get_file_diff_at_commit', { vaultPath, path, commitHash })</span>
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > return mockInvoke<string>('get_file_diff_at_commit', { path, commitHash })</span>
|
||||
}
|
||||
}, [vaultPath])
|
||||
const loadDiffAtCommit = useCallback((path: string, commitHash: string): Promise<string> =>
|
||||
tauriCall<string>('get_file_diff_at_commit', { vaultPath, path, commitHash }, { path, commitHash }), [vaultPath])
|
||||
|
||||
const loadDiff = useCallback(<span class="fstat-no" title="function not covered" >async (p</span>ath: string): Promise<string> => {
|
||||
<span class="cstat-no" title="statement not covered" > if (isTauri()) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return invoke<string>('get_file_diff', { vaultPath, path })</span>
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > return mockInvoke<string>('get_file_diff', { path })</span>
|
||||
}
|
||||
}, [vaultPath])
|
||||
const loadDiff = useCallback((path: string): Promise<string> =>
|
||||
tauriCall<string>('get_file_diff', { vaultPath, path }, { path }), [vaultPath])
|
||||
|
||||
const isFileModified = useCallback(<span class="fstat-no" title="function not covered" >(p</span>ath: string): boolean => {
|
||||
<span class="cstat-no" title="statement not covered" > return modifiedFiles.some(<span class="fstat-no" title="function not covered" >(f</span>) => <span class="cstat-no" title="statement not covered" >f.path === path)</span></span>
|
||||
}, [modifiedFiles])
|
||||
const getNoteStatus = useCallback((path: string): NoteStatus =>
|
||||
resolveNoteStatus(path, tracker.newPaths, modifiedFiles), [tracker.newPaths, modifiedFiles])
|
||||
|
||||
const commitAndPush = useCallback(<span class="fstat-no" title="function not covered" >async (m</span>essage: string): Promise<string> => {
|
||||
<span class="cstat-no" title="statement not covered" > if (isTauri()) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > await invoke<string>('git_commit', { vaultPath, message })</span>
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
<span class="cstat-no" title="statement not covered" > await invoke<string>('git_push', { vaultPath })</span>
|
||||
<span class="cstat-no" title="statement not covered" > return 'Committed and pushed'</span>
|
||||
} catch {
|
||||
<span class="cstat-no" title="statement not covered" > return 'Committed (push failed)'</span>
|
||||
}
|
||||
} else {
|
||||
<span class="cstat-no" title="statement not covered" > await mockInvoke<string>('git_commit', { message })</span>
|
||||
<span class="cstat-no" title="statement not covered" > await mockInvoke<string>('git_push', {})</span>
|
||||
<span class="cstat-no" title="statement not covered" > return 'Committed and pushed'</span>
|
||||
}
|
||||
}, [vaultPath])
|
||||
const commitAndPush = useCallback((message: string): Promise<string> =>
|
||||
commitWithPush(vaultPath, message), [vaultPath])
|
||||
|
||||
return {
|
||||
entries,
|
||||
allContent,
|
||||
modifiedFiles,
|
||||
addEntry,
|
||||
updateEntry,
|
||||
updateContent,
|
||||
loadModifiedFiles,
|
||||
loadGitHistory,
|
||||
loadDiff,
|
||||
loadDiffAtCommit,
|
||||
isFileModified,
|
||||
commitAndPush,
|
||||
entries, allContent, modifiedFiles,
|
||||
addEntry, updateEntry, removeEntry, replaceEntry, updateContent,
|
||||
loadModifiedFiles, loadGitHistory, loadDiff, loadDiffAtCommit,
|
||||
getNoteStatus, commitAndPush,
|
||||
}
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
@@ -481,7 +451,7 @@ export function useVaultLoader(vaultPath: string) {
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">40.9% </span>
|
||||
<span class="strong">47.5% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>45/110</span>
|
||||
<span class='fraction'>57/120</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">50% </span>
|
||||
<span class="strong">40.47% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>11/22</span>
|
||||
<span class='fraction'>17/42</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">13.51% </span>
|
||||
<span class="strong">20.58% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>5/37</span>
|
||||
<span class='fraction'>7/34</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">45% </span>
|
||||
<span class="strong">57.73% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>45/100</span>
|
||||
<span class='fraction'>56/97</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -80,32 +80,17 @@
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td class="file low" data-value="App.tsx"><a href="App.tsx.html">App.tsx</a></td>
|
||||
<td data-value="40.9" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 40%"></div><div class="cover-empty" style="width: 60%"></div></div>
|
||||
<td data-value="47.5" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 47%"></div><div class="cover-empty" style="width: 53%"></div></div>
|
||||
</td>
|
||||
<td data-value="40.9" class="pct low">40.9%</td>
|
||||
<td data-value="110" class="abs low">45/110</td>
|
||||
<td data-value="50" class="pct medium">50%</td>
|
||||
<td data-value="22" class="abs medium">11/22</td>
|
||||
<td data-value="13.51" class="pct low">13.51%</td>
|
||||
<td data-value="37" class="abs low">5/37</td>
|
||||
<td data-value="45" class="pct low">45%</td>
|
||||
<td data-value="100" class="abs low">45/100</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file empty" data-value="types.ts"><a href="types.ts.html">types.ts</a></td>
|
||||
<td data-value="0" class="pic empty">
|
||||
<div class="chart"><div class="cover-fill" style="width: 0%"></div><div class="cover-empty" style="width: 100%"></div></div>
|
||||
</td>
|
||||
<td data-value="0" class="pct empty">0%</td>
|
||||
<td data-value="0" class="abs empty">0/0</td>
|
||||
<td data-value="0" class="pct empty">0%</td>
|
||||
<td data-value="0" class="abs empty">0/0</td>
|
||||
<td data-value="0" class="pct empty">0%</td>
|
||||
<td data-value="0" class="abs empty">0/0</td>
|
||||
<td data-value="0" class="pct empty">0%</td>
|
||||
<td data-value="0" class="abs empty">0/0</td>
|
||||
<td data-value="47.5" class="pct low">47.5%</td>
|
||||
<td data-value="120" class="abs low">57/120</td>
|
||||
<td data-value="40.47" class="pct low">40.47%</td>
|
||||
<td data-value="42" class="abs low">17/42</td>
|
||||
<td data-value="20.58" class="pct low">20.58%</td>
|
||||
<td data-value="34" class="abs low">7/34</td>
|
||||
<td data-value="57.73" class="pct medium">57.73%</td>
|
||||
<td data-value="97" class="abs medium">56/97</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
@@ -116,7 +101,7 @@
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
<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">1714x</span>
|
||||
<span class="cline-any cline-yes">30814x</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 { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
@@ -88,7 +88,7 @@ export function cn(...inputs: ClassValue[]) {
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for src/types.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</a> types.ts</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>0/0</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/0</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/0</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>0/0</span>
|
||||
</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></td><td class="line-coverage quiet"><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>
|
||||
<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></td><td class="text"><pre class="prettyprint lang-js">export interface VaultEntry {
|
||||
path: string
|
||||
filename: string
|
||||
title: string
|
||||
isA: string | null
|
||||
aliases: string[]
|
||||
belongsTo: string[]
|
||||
relatedTo: string[]
|
||||
status: string | null
|
||||
owner: string | null
|
||||
cadence: string | null
|
||||
archived: boolean
|
||||
trashed: boolean
|
||||
trashedAt: number | null
|
||||
modifiedAt: number | null
|
||||
createdAt: number | null
|
||||
fileSize: number
|
||||
snippet: string
|
||||
/** Generic relationship fields: any frontmatter key whose value contains wikilinks. */
|
||||
relationships: Record<string, string[]>
|
||||
/** Phosphor icon name (kebab-case) for Type entries, e.g. "cooking-pot" */
|
||||
icon: string | null
|
||||
/** Accent color key for Type entries: "red" | "purple" | "blue" | "green" | "yellow" | "orange" */
|
||||
color: string | null
|
||||
/** Display order for Type entries in sidebar (lower = higher). null = use default order. */
|
||||
order: number | null
|
||||
}
|
||||
|
||||
export interface GitCommit {
|
||||
hash: string
|
||||
shortHash: string
|
||||
message: string
|
||||
author: string
|
||||
date: number // unix timestamp
|
||||
}
|
||||
|
||||
export interface ModifiedFile {
|
||||
path: string
|
||||
relativePath: string
|
||||
status: 'modified' | 'added' | 'deleted' | 'untracked' | 'renamed'
|
||||
}
|
||||
|
||||
export type SidebarSelection =
|
||||
| { kind: 'filter'; filter: 'all' | 'favorites' | 'archived' | 'trash' }
|
||||
| { kind: 'sectionGroup'; type: string }
|
||||
| { kind: 'entity'; entry: VaultEntry }
|
||||
| { kind: 'topic'; entry: VaultEntry }
|
||||
</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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
</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>
|
||||
|
||||
@@ -1,655 +0,0 @@
|
||||
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<title>Code coverage report for src/utils/ai-chat.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/utils</a> ai-chat.ts</h1>
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">5.26% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>4/76</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>0/43</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">0% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>0/10</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">5.63% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>4/71</span>
|
||||
</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></td><td class="line-coverage quiet"><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-yes">2x</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-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-no"> </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-yes">2x</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-no"> </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-no"> </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-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-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-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-no"> </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-no"> </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-no"> </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-yes">2x</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-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-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-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-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-no"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<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-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-neutral"> </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>
|
||||
<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-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-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>
|
||||
<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>
|
||||
<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>
|
||||
<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-no"> </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-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>
|
||||
<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-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-yes">2x</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></td><td class="text"><pre class="prettyprint lang-js">/**
|
||||
* AI Chat utilities — Anthropic API client, token estimation, context building.
|
||||
*/
|
||||
|
||||
import type { VaultEntry } from '../types'
|
||||
|
||||
// --- localStorage key for API key ---
|
||||
const API_KEY_STORAGE_KEY = 'laputa:anthropic-api-key'
|
||||
|
||||
export function <span class="fstat-no" title="function not covered" >getApiKey(): string {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return localStorage.getItem(API_KEY_STORAGE_KEY) ?? ''</span>
|
||||
}
|
||||
|
||||
export function <span class="fstat-no" title="function not covered" >setApiKey(k</span>ey: string): void {
|
||||
<span class="cstat-no" title="statement not covered" > localStorage.setItem(API_KEY_STORAGE_KEY, key)</span>
|
||||
}
|
||||
|
||||
// --- Token estimation ---
|
||||
|
||||
/** Rough token estimate: ~4 chars per token for English text. */
|
||||
export function <span class="fstat-no" title="function not covered" >estimateTokens(t</span>ext: string | number): number {
|
||||
const len = <span class="cstat-no" title="statement not covered" >typeof text === 'number' ? text : text.length</span>
|
||||
<span class="cstat-no" title="statement not covered" > return Math.ceil(len / 4)</span>
|
||||
}
|
||||
|
||||
const DEFAULT_CONTEXT_LIMIT = 180_000
|
||||
|
||||
export function <span class="fstat-no" title="function not covered" >getContextLimit(_</span>model: string): number {
|
||||
<span class="cstat-no" title="statement not covered" > return DEFAULT_CONTEXT_LIMIT</span>
|
||||
}
|
||||
|
||||
// --- Context building ---
|
||||
|
||||
/** Build system prompt from selected context notes. */
|
||||
export function <span class="fstat-no" title="function not covered" >buildSystemPrompt(</span>
|
||||
notes: VaultEntry[],
|
||||
allContent: Record<string, string>,
|
||||
model: string,
|
||||
): { prompt: string; totalTokens: number; truncated: boolean } {
|
||||
<span class="cstat-no" title="statement not covered" > if (notes.length === 0) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return { prompt: '', totalTokens: 0, truncated: false }</span>
|
||||
}
|
||||
|
||||
const contextBudget = <span class="cstat-no" title="statement not covered" >Math.floor(getContextLimit(model) * 0.6)</span>
|
||||
const preamble = <span class="cstat-no" title="statement not covered" >[</span>
|
||||
'You are a helpful AI assistant integrated into Laputa, a personal knowledge management app.',
|
||||
'The user has selected the following notes as context. Use them to answer questions accurately.',
|
||||
'',
|
||||
].join('\n')
|
||||
|
||||
const parts: string[] = <span class="cstat-no" title="statement not covered" >[preamble]</span>
|
||||
let totalChars = <span class="cstat-no" title="statement not covered" >preamble.length</span>
|
||||
let truncated = <span class="cstat-no" title="statement not covered" >false</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > for (const note of notes) {</span>
|
||||
const content = <span class="cstat-no" title="statement not covered" >allContent[note.path] ?? ''</span>
|
||||
const header = <span class="cstat-no" title="statement not covered" >`--- Note: ${note.title} (${note.isA ?? 'Note'}) ---`</span>
|
||||
const noteText = <span class="cstat-no" title="statement not covered" >`${header}\n${content}\n`</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > if (estimateTokens(totalChars + noteText.length) > contextBudget) {</span>
|
||||
const <span class="cstat-no" title="statement not covered" >remaining = (contextBudget - estimateTokens(totalChars)) * 4</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (remaining > 200) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > parts.push(`${header}\n${content.slice(0, remaining)}\n[... truncated ...]`)</span>
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > truncated = true</span>
|
||||
<span class="cstat-no" title="statement not covered" > break</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > parts.push(noteText)</span>
|
||||
<span class="cstat-no" title="statement not covered" > totalChars += noteText.length</span>
|
||||
}
|
||||
|
||||
const prompt = <span class="cstat-no" title="statement not covered" >parts.join('\n')</span>
|
||||
<span class="cstat-no" title="statement not covered" > return { prompt, totalTokens: estimateTokens(prompt), truncated }</span>
|
||||
}
|
||||
|
||||
// --- API types ---
|
||||
|
||||
export interface ChatMessage {
|
||||
role: 'user' | 'assistant'
|
||||
content: string
|
||||
id: string
|
||||
}
|
||||
|
||||
let msgIdCounter = 0
|
||||
export function <span class="fstat-no" title="function not covered" >nextMessageId(): string {</span>
|
||||
<span class="cstat-no" title="statement not covered" > return `msg-${++msgIdCounter}-${Date.now()}`</span>
|
||||
}
|
||||
|
||||
// --- SSE parsing ---
|
||||
|
||||
function <span class="fstat-no" title="function not covered" >parseSseEvent(l</span>ine: string, onChunk: (text: string) => void): boolean {
|
||||
<span class="cstat-no" title="statement not covered" > if (!line.startsWith('data: ')) <span class="cstat-no" title="statement not covered" >return false</span></span>
|
||||
const data = <span class="cstat-no" title="statement not covered" >line.slice(6)</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (data === '[DONE]') <span class="cstat-no" title="statement not covered" >return true</span></span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
const event = <span class="cstat-no" title="statement not covered" >JSON.parse(data)</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (event.type === 'content_block_delta' && event.delta?.text) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onChunk(event.delta.text)</span>
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > if (event.type === 'message_stop') <span class="cstat-no" title="statement not covered" >return true</span></span>
|
||||
} catch {
|
||||
// skip malformed events
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > return false</span>
|
||||
}
|
||||
|
||||
async function <span class="fstat-no" title="function not covered" >readSseStream(</span>
|
||||
reader: ReadableStreamDefaultReader<Uint8Array>,
|
||||
onChunk: (text: string) => void,
|
||||
): Promise<void> {
|
||||
const decoder = <span class="cstat-no" title="statement not covered" >new TextDecoder()</span>
|
||||
let buffer = <span class="cstat-no" title="statement not covered" >''</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > while (true) {</span>
|
||||
const { value, done } = <span class="cstat-no" title="statement not covered" >await reader.read()</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (done) <span class="cstat-no" title="statement not covered" >break</span></span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > buffer += decoder.decode(value, { stream: true })</span>
|
||||
const lines = <span class="cstat-no" title="statement not covered" >buffer.split('\n')</span>
|
||||
<span class="cstat-no" title="statement not covered" > buffer = lines.pop() ?? ''</span>
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > for (const line of lines) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (parseSseEvent(line, onChunk)) <span class="cstat-no" title="statement not covered" >return</span></span>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function <span class="fstat-no" title="function not covered" >parseApiError(r</span>esponse: Response): Promise<string> {
|
||||
const errText = <span class="cstat-no" title="statement not covered" >await response.text()</span>
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
const errJson = <span class="cstat-no" title="statement not covered" >JSON.parse(errText)</span>
|
||||
<span class="cstat-no" title="statement not covered" > return errJson.error?.message || errJson.error || `API error (${response.status})`</span>
|
||||
} catch {
|
||||
<span class="cstat-no" title="statement not covered" > return `API error (${response.status})`</span>
|
||||
}
|
||||
}
|
||||
|
||||
// --- Streaming API call ---
|
||||
|
||||
export async function <span class="fstat-no" title="function not covered" >streamChat(</span>
|
||||
messages: { role: 'user' | 'assistant'; content: string }[],
|
||||
systemPrompt: string,
|
||||
model: string,
|
||||
onChunk: (text: string) => void,
|
||||
onDone: () => void,
|
||||
onError: (error: string) => void,
|
||||
): Promise<void> {
|
||||
const apiKey = <span class="cstat-no" title="statement not covered" >getApiKey()</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (!apiKey) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onError('No API key configured. Click the key icon to set your Anthropic API key.')</span>
|
||||
<span class="cstat-no" title="statement not covered" > return</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > try {</span>
|
||||
const response = <span class="cstat-no" title="statement not covered" >await fetch('/api/ai/chat', {</span>
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
apiKey, model, messages,
|
||||
system: systemPrompt || undefined,
|
||||
maxTokens: 4096,
|
||||
}),
|
||||
})
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > if (!response.ok) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onError(await parseApiError(response))</span>
|
||||
<span class="cstat-no" title="statement not covered" > return</span>
|
||||
}
|
||||
|
||||
const reader = <span class="cstat-no" title="statement not covered" >response.body?.getReader()</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (!reader) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > onError('No response body')</span>
|
||||
<span class="cstat-no" title="statement not covered" > return</span>
|
||||
}
|
||||
|
||||
<span class="cstat-no" title="statement not covered" > await readSseStream(reader, onChunk)</span>
|
||||
<span class="cstat-no" title="statement not covered" > onDone()</span>
|
||||
} catch (err: any) {
|
||||
<span class="cstat-no" title="statement not covered" > onError(err.message || 'Network error')</span>
|
||||
}
|
||||
}
|
||||
|
||||
// --- Model options ---
|
||||
export const MODEL_OPTIONS = [
|
||||
{ value: 'claude-3-5-haiku-20241022', label: 'Haiku 3.5' },
|
||||
{ value: 'claude-sonnet-4-20250514', label: 'Sonnet 4' },
|
||||
{ value: 'claude-opus-4-20250514', label: 'Opus 4' },
|
||||
] as const
|
||||
</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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
</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>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">88.37% </span>
|
||||
<span class="strong">93.33% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>38/43</span>
|
||||
<span class='fraction'>42/45</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">81.08% </span>
|
||||
<span class="strong">90.9% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>30/37</span>
|
||||
<span class='fraction'>30/33</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>2/2</span>
|
||||
<span class='fraction'>7/7</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">90% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>36/40</span>
|
||||
<span class='fraction'>35/35</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -131,81 +131,73 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<a name='L69'></a><a href='#L69'>69</a></td><td class="line-coverage quiet"><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-yes">207x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">26x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">54x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">146x</span>
|
||||
<span class="cline-any cline-yes">146x</span>
|
||||
<span class="cline-any cline-yes">28x</span>
|
||||
<span class="cline-any cline-yes">171x</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-yes">54x</span>
|
||||
<span class="cline-any cline-yes">18x</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-yes">117x</span>
|
||||
<span class="cline-any cline-yes">117x</span>
|
||||
<span class="cline-any cline-yes">117x</span>
|
||||
<span class="cline-any cline-yes">117x</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-yes">41x</span>
|
||||
<span class="cline-any cline-yes">28x</span>
|
||||
<span class="cline-any cline-yes">28x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">27x</span>
|
||||
<span class="cline-any cline-yes">27x</span>
|
||||
<span class="cline-any cline-yes">27x</span>
|
||||
<span class="cline-any cline-yes">27x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">27x</span>
|
||||
<span class="cline-any cline-yes">207x</span>
|
||||
<span class="cline-any cline-yes">207x</span>
|
||||
<span class="cline-any cline-yes">36x</span>
|
||||
<span class="cline-any cline-yes">36x</span>
|
||||
<span class="cline-any cline-yes">36x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">118x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-yes">171x</span>
|
||||
<span class="cline-any cline-yes">18x</span>
|
||||
<span class="cline-any cline-yes">18x</span>
|
||||
<span class="cline-any cline-yes">18x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">118x</span>
|
||||
<span class="cline-any cline-yes">118x</span>
|
||||
<span class="cline-any cline-yes">118x</span>
|
||||
<span class="cline-any cline-yes">118x</span>
|
||||
<span class="cline-any cline-yes">171x</span>
|
||||
<span class="cline-any cline-yes">171x</span>
|
||||
<span class="cline-any cline-yes">171x</span>
|
||||
<span class="cline-any cline-yes">171x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">118x</span>
|
||||
<span class="cline-any cline-yes">28x</span>
|
||||
<span class="cline-any cline-yes">171x</span>
|
||||
<span class="cline-any cline-yes">135x</span>
|
||||
<span class="cline-any cline-yes">117x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">90x</span>
|
||||
<span class="cline-any cline-yes">42x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">76x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">76x</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">76x</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>
|
||||
<span class="cline-any cline-yes">76x</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-yes">16x</span>
|
||||
<span class="cline-any cline-yes">14x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">27x</span>
|
||||
<span class="cline-any cline-yes">27x</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 type { FrontmatterValue } from '../components/Inspector'
|
||||
|
||||
@@ -213,70 +205,66 @@ export interface ParsedFrontmatter {
|
||||
[key: string]: FrontmatterValue
|
||||
}
|
||||
|
||||
function unquote(s: string): string {
|
||||
return s.replace(/^["']|["']$/g, '')
|
||||
}
|
||||
|
||||
function collapseList(items: string[]): FrontmatterValue {
|
||||
return items.length === 1 ? items[0] : items
|
||||
}
|
||||
|
||||
function isBlockScalar(value: string): boolean {
|
||||
return value === '' || value === '|' || value === '>'
|
||||
}
|
||||
|
||||
function parseInlineArray(value: string): FrontmatterValue {
|
||||
const items = value.slice(1, -1).split(',').map(s => unquote(s.trim()))
|
||||
return collapseList(items)
|
||||
}
|
||||
|
||||
function parseScalar(value: string): FrontmatterValue {
|
||||
const clean = unquote(value)
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (clean.toLowerCase() === 'true') <span class="cstat-no" title="statement not covered" >return true</span>
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (clean.toLowerCase() === 'false') <span class="cstat-no" title="statement not covered" >return false</span>
|
||||
return clean
|
||||
}
|
||||
|
||||
/** Parse YAML frontmatter from content */
|
||||
export function parseFrontmatter(content: string | null): ParsedFrontmatter {
|
||||
if (!content) return {}
|
||||
|
||||
const match = content.match(/^---\n([\s\S]*?)\n---/)
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (!match) <span class="cstat-no" title="statement not covered" >return {}</span>
|
||||
if (!match) return {}
|
||||
|
||||
const yaml = match[1]
|
||||
const result: ParsedFrontmatter = {}
|
||||
|
||||
let currentKey: string | null = null
|
||||
let currentList: string[] = []
|
||||
let inList = false
|
||||
|
||||
const lines = yaml.split('\n')
|
||||
|
||||
for (const line of lines) {
|
||||
const listMatch = line.match(/^ - (.*)$/)
|
||||
for (const line of match[1].split('\n')) {
|
||||
const listMatch = line.match(/^ {2}- (.*)$/)
|
||||
if (listMatch && currentKey) {
|
||||
inList = true
|
||||
currentList.push(listMatch[1].replace(/^["']|["']$/g, ''))
|
||||
currentList.push(unquote(listMatch[1]))
|
||||
continue
|
||||
}
|
||||
|
||||
if (inList && currentKey) {
|
||||
result[currentKey] = currentList.length === 1 ? currentList[0] : <span class="branch-1 cbranch-no" title="branch not covered" >currentList</span>
|
||||
result[currentKey] = collapseList(currentList)
|
||||
currentList = []
|
||||
inList = false
|
||||
}
|
||||
|
||||
const kvMatch = line.match(/^["']?([^"':]+)["']?\s*:\s*(.*)$/)
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (kvMatch) {
|
||||
currentKey = kvMatch[1].trim()
|
||||
const value = kvMatch[2].trim()
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (!kvMatch) <span class="cstat-no" title="statement not covered" >continue</span>
|
||||
currentKey = kvMatch[1].trim()
|
||||
const value = kvMatch[2].trim()
|
||||
|
||||
if (value === '' || value === '|' || value === '>') {
|
||||
continue
|
||||
}
|
||||
|
||||
if (value.startsWith('[') && value.endsWith(']')) {
|
||||
const items = value.slice(1, -1).split(',').map(s => s.trim().replace(/^["']|["']$/g, ''))
|
||||
result[currentKey] = items.length === 1 ? <span class="branch-0 cbranch-no" title="branch not covered" >items[0] : i</span>tems
|
||||
continue
|
||||
}
|
||||
|
||||
const unquoted = value.replace(/^["']|["']$/g, '')
|
||||
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (unquoted.toLowerCase() === 'true') {
|
||||
<span class="cstat-no" title="statement not covered" > result[currentKey] = true</span>
|
||||
<span class="cstat-no" title="statement not covered" > continue</span>
|
||||
}
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (unquoted.toLowerCase() === 'false') {
|
||||
<span class="cstat-no" title="statement not covered" > result[currentKey] = false</span>
|
||||
<span class="cstat-no" title="statement not covered" > continue</span>
|
||||
}
|
||||
|
||||
result[currentKey] = unquoted
|
||||
}
|
||||
}
|
||||
|
||||
if (inList && currentKey) {
|
||||
result[currentKey] = currentList.length === 1 ? currentList[0] : <span class="branch-1 cbranch-no" title="branch not covered" >currentList</span>
|
||||
if (isBlockScalar(value)) continue
|
||||
if (value.startsWith('[') && value.endsWith(']')) { result[currentKey] = parseInlineArray(value); continue }
|
||||
result[currentKey] = parseScalar(value)
|
||||
}
|
||||
|
||||
if (inList && currentKey) result[currentKey] = collapseList(currentList)
|
||||
return result
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
@@ -286,7 +274,7 @@ export function parseFrontmatter(content: string | null): ParsedFrontmatter {
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">46.85% </span>
|
||||
<span class="strong">91.69% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>82/175</span>
|
||||
<span class='fraction'>320/349</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">45.37% </span>
|
||||
<span class="strong">85.93% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>54/119</span>
|
||||
<span class='fraction'>226/263</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">52.17% </span>
|
||||
<span class="strong">96.51% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>12/23</span>
|
||||
<span class='fraction'>83/86</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">49.37% </span>
|
||||
<span class="strong">92.6% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>79/160</span>
|
||||
<span class='fraction'>263/284</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line low'></div>
|
||||
<div class='status-line high'></div>
|
||||
<div class="pad1">
|
||||
<table class="coverage-summary">
|
||||
<thead>
|
||||
@@ -79,44 +79,89 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody><tr>
|
||||
<td class="file low" data-value="ai-chat.ts"><a href="ai-chat.ts.html">ai-chat.ts</a></td>
|
||||
<td data-value="5.26" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 5%"></div><div class="cover-empty" style="width: 95%"></div></div>
|
||||
<td class="file high" data-value="frontmatter.ts"><a href="frontmatter.ts.html">frontmatter.ts</a></td>
|
||||
<td data-value="93.33" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 93%"></div><div class="cover-empty" style="width: 7%"></div></div>
|
||||
</td>
|
||||
<td data-value="5.26" class="pct low">5.26%</td>
|
||||
<td data-value="76" class="abs low">4/76</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="43" class="abs low">0/43</td>
|
||||
<td data-value="0" class="pct low">0%</td>
|
||||
<td data-value="10" class="abs low">0/10</td>
|
||||
<td data-value="5.63" class="pct low">5.63%</td>
|
||||
<td data-value="71" class="abs low">4/71</td>
|
||||
<td data-value="93.33" class="pct high">93.33%</td>
|
||||
<td data-value="45" class="abs high">42/45</td>
|
||||
<td data-value="90.9" class="pct high">90.9%</td>
|
||||
<td data-value="33" class="abs high">30/33</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="7" class="abs high">7/7</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="35" class="abs high">35/35</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="frontmatter.ts"><a href="frontmatter.ts.html">frontmatter.ts</a></td>
|
||||
<td data-value="88.37" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 88%"></div><div class="cover-empty" style="width: 12%"></div></div>
|
||||
<td class="file high" data-value="fuzzyMatch.ts"><a href="fuzzyMatch.ts.html">fuzzyMatch.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="88.37" class="pct high">88.37%</td>
|
||||
<td data-value="43" class="abs high">38/43</td>
|
||||
<td data-value="81.08" class="pct high">81.08%</td>
|
||||
<td data-value="37" class="abs high">30/37</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="16" class="abs high">16/16</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="11" class="abs high">11/11</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="1" class="abs high">1/1</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="13" class="abs high">13/13</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="iconRegistry.ts"><a href="iconRegistry.ts.html">iconRegistry.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="4" class="abs high">4/4</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="3" class="abs high">3/3</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="2" class="abs high">2/2</td>
|
||||
<td data-value="90" class="pct high">90%</td>
|
||||
<td data-value="40" class="abs high">36/40</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="4" class="abs high">4/4</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="noteListHelpers.ts"><a href="noteListHelpers.ts.html">noteListHelpers.ts</a></td>
|
||||
<td data-value="86.23" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 86%"></div><div class="cover-empty" style="width: 14%"></div></div>
|
||||
</td>
|
||||
<td data-value="86.23" class="pct high">86.23%</td>
|
||||
<td data-value="138" class="abs high">119/138</td>
|
||||
<td data-value="77.87" class="pct medium">77.87%</td>
|
||||
<td data-value="113" class="abs medium">88/113</td>
|
||||
<td data-value="95.12" class="pct high">95.12%</td>
|
||||
<td data-value="41" class="abs high">39/41</td>
|
||||
<td data-value="86.79" class="pct high">86.79%</td>
|
||||
<td data-value="106" class="abs high">92/106</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="personMentionSuggestions.ts"><a href="personMentionSuggestions.ts.html">personMentionSuggestions.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="6" class="abs high">6/6</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="5" class="abs high">5/5</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="2" class="abs high">2/2</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="5" class="abs high">5/5</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="typeColors.ts"><a href="typeColors.ts.html">typeColors.ts</a></td>
|
||||
<td data-value="86.66" class="pic high">
|
||||
<div class="chart"><div class="cover-fill" style="width: 86%"></div><div class="cover-empty" style="width: 14%"></div></div>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="86.66" class="pct high">86.66%</td>
|
||||
<td data-value="15" class="abs high">13/15</td>
|
||||
<td data-value="71.42" class="pct medium">71.42%</td>
|
||||
<td data-value="14" class="abs medium">10/14</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="15" class="abs high">15/15</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="14" class="abs high">14/14</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="4" class="abs high">4/4</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
@@ -124,18 +169,93 @@
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file medium" data-value="wikilinks.ts"><a href="wikilinks.ts.html">wikilinks.ts</a></td>
|
||||
<td data-value="65.85" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 65%"></div><div class="cover-empty" style="width: 35%"></div></div>
|
||||
<td class="file medium" data-value="url.ts"><a href="url.ts.html">url.ts</a></td>
|
||||
<td data-value="66.66" class="pic medium">
|
||||
<div class="chart"><div class="cover-fill" style="width: 66%"></div><div class="cover-empty" style="width: 34%"></div></div>
|
||||
</td>
|
||||
<td data-value="65.85" class="pct medium">65.85%</td>
|
||||
<td data-value="41" class="abs medium">27/41</td>
|
||||
<td data-value="56" class="pct medium">56%</td>
|
||||
<td data-value="25" class="abs medium">14/25</td>
|
||||
<td data-value="85.71" class="pct high">85.71%</td>
|
||||
<td data-value="7" class="abs high">6/7</td>
|
||||
<td data-value="72.22" class="pct medium">72.22%</td>
|
||||
<td data-value="36" class="abs medium">26/36</td>
|
||||
<td data-value="66.66" class="pct medium">66.66%</td>
|
||||
<td data-value="12" class="abs medium">8/12</td>
|
||||
<td data-value="75" class="pct medium">75%</td>
|
||||
<td data-value="8" class="abs medium">6/8</td>
|
||||
<td data-value="66.66" class="pct medium">66.66%</td>
|
||||
<td data-value="3" class="abs medium">2/3</td>
|
||||
<td data-value="60" class="pct medium">60%</td>
|
||||
<td data-value="10" class="abs medium">6/10</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file low" data-value="vault-dialog.ts"><a href="vault-dialog.ts.html">vault-dialog.ts</a></td>
|
||||
<td data-value="40" class="pic low">
|
||||
<div class="chart"><div class="cover-fill" style="width: 40%"></div><div class="cover-empty" style="width: 60%"></div></div>
|
||||
</td>
|
||||
<td data-value="40" class="pct low">40%</td>
|
||||
<td data-value="5" class="abs low">2/5</td>
|
||||
<td data-value="50" class="pct medium">50%</td>
|
||||
<td data-value="6" class="abs medium">3/6</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="1" class="abs high">1/1</td>
|
||||
<td data-value="40" class="pct low">40%</td>
|
||||
<td data-value="5" class="abs low">2/5</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="wikilink.ts"><a href="wikilink.ts.html">wikilink.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="10" class="abs high">10/10</td>
|
||||
<td data-value="83.33" class="pct high">83.33%</td>
|
||||
<td data-value="6" class="abs high">5/6</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="3" class="abs high">3/3</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="8" class="abs high">8/8</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="wikilinkColors.ts"><a href="wikilinkColors.ts.html">wikilinkColors.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="15" class="abs high">15/15</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="13" class="abs high">13/13</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="5" class="abs high">5/5</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="11" class="abs high">11/11</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="wikilinkSuggestions.ts"><a href="wikilinkSuggestions.ts.html">wikilinkSuggestions.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="22" class="abs high">22/22</td>
|
||||
<td data-value="86.66" class="pct high">86.66%</td>
|
||||
<td data-value="15" class="abs high">13/15</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="6" class="abs high">6/6</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="19" class="abs high">19/19</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td class="file high" data-value="wikilinks.ts"><a href="wikilinks.ts.html">wikilinks.ts</a></td>
|
||||
<td data-value="100" class="pic high">
|
||||
<div class="chart"><div class="cover-fill cover-full" style="width: 100%"></div><div class="cover-empty" style="width: 0%"></div></div>
|
||||
</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="61" class="abs high">61/61</td>
|
||||
<td data-value="97.22" class="pct high">97.22%</td>
|
||||
<td data-value="36" class="abs high">35/36</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="11" class="abs high">11/11</td>
|
||||
<td data-value="100" class="pct high">100%</td>
|
||||
<td data-value="55" class="abs high">55/55</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
@@ -146,7 +266,7 @@
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,16 +23,16 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">86.66% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>13/15</span>
|
||||
<span class='fraction'>15/15</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">71.42% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>10/14</span>
|
||||
<span class='fraction'>14/14</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -121,12 +121,14 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<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></td><td class="line-coverage quiet"><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-yes">5x</span>
|
||||
<span class="cline-any cline-yes">13x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -137,7 +139,7 @@
|
||||
<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">5x</span>
|
||||
<span class="cline-any cline-yes">13x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -148,11 +150,11 @@
|
||||
<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">5x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">13x</span>
|
||||
<span class="cline-any cline-yes">13x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">13x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
@@ -161,23 +163,25 @@
|
||||
<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">5x</span>
|
||||
<span class="cline-any cline-yes">30x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-yes">30x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">13x</span>
|
||||
<span class="cline-any cline-yes">104x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">13x</span>
|
||||
<span class="cline-any cline-yes">104x</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-yes">832x</span>
|
||||
<span class="cline-any cline-yes">832x</span>
|
||||
<span class="cline-any cline-yes">13224x</span>
|
||||
<span class="cline-any cline-yes">13206x</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-yes">758x</span>
|
||||
<span class="cline-any cline-yes">758x</span>
|
||||
<span class="cline-any cline-yes">12965x</span>
|
||||
<span class="cline-any cline-yes">12962x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">/**
|
||||
* Maps note types to their accent color CSS variables.
|
||||
@@ -206,8 +210,8 @@ const TYPE_LIGHT_COLOR_MAP: Record<string, string> = {
|
||||
Type: 'var(--accent-blue-light)',
|
||||
}
|
||||
|
||||
const DEFAULT_COLOR = 'var(--accent-blue)'
|
||||
const DEFAULT_LIGHT_COLOR = 'var(--accent-blue-light)'
|
||||
const DEFAULT_COLOR = 'var(--muted-foreground)'
|
||||
const DEFAULT_LIGHT_COLOR = 'var(--muted)'
|
||||
|
||||
/** Color key → CSS variable mapping for the design system accent palette */
|
||||
export const ACCENT_COLORS: { key: string; label: string; css: string; cssLight: string }[] = [
|
||||
@@ -217,6 +221,8 @@ export const ACCENT_COLORS: { key: string; label: string; css: string; cssLight:
|
||||
{ key: 'green', label: 'Green', css: 'var(--accent-green)', cssLight: 'var(--accent-green-light)' },
|
||||
{ key: 'blue', label: 'Blue', css: 'var(--accent-blue)', cssLight: 'var(--accent-blue-light)' },
|
||||
{ key: 'purple', label: 'Purple', css: 'var(--accent-purple)', cssLight: 'var(--accent-purple-light)' },
|
||||
{ key: 'teal', label: 'Teal', css: 'var(--accent-teal)', cssLight: 'var(--accent-teal-light)' },
|
||||
{ key: 'pink', label: 'Pink', css: 'var(--accent-pink)', cssLight: 'var(--accent-pink-light)' },
|
||||
]
|
||||
|
||||
const COLOR_KEY_TO_CSS: Record<string, string> = Object.fromEntries(
|
||||
@@ -228,13 +234,13 @@ const COLOR_KEY_TO_CSS_LIGHT: Record<string, string> = Object.fromEntries(
|
||||
|
||||
/** Returns the CSS variable for the accent color of a given note type, with optional custom override */
|
||||
export function getTypeColor(isA: string | null, customColorKey?: string | null): string {
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (customColorKey && <span class="branch-1 cbranch-no" title="branch not covered" >COLOR_KEY_TO_CSS[customColorKey]) <span class="cstat-no" title="statement not covered" >r</span>eturn COLOR_KEY_TO_CSS[customColorKey]</span>
|
||||
if (customColorKey && COLOR_KEY_TO_CSS[customColorKey]) return COLOR_KEY_TO_CSS[customColorKey]
|
||||
return (isA && TYPE_COLOR_MAP[isA]) ?? DEFAULT_COLOR
|
||||
}
|
||||
|
||||
/** Returns the CSS variable for the light/background variant of a given note type's color */
|
||||
export function getTypeLightColor(isA: string | null, customColorKey?: string | null): string {
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (customColorKey && <span class="branch-1 cbranch-no" title="branch not covered" >COLOR_KEY_TO_CSS_LIGHT[customColorKey]) <span class="cstat-no" title="statement not covered" >r</span>eturn COLOR_KEY_TO_CSS_LIGHT[customColorKey]</span>
|
||||
if (customColorKey && COLOR_KEY_TO_CSS_LIGHT[customColorKey]) return COLOR_KEY_TO_CSS_LIGHT[customColorKey]
|
||||
return (isA && TYPE_LIGHT_COLOR_MAP[isA]) ?? DEFAULT_LIGHT_COLOR
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
@@ -244,7 +250,7 @@ export function getTypeLightColor(isA: string | null, customColorKey?: string |
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
@@ -23,30 +23,30 @@
|
||||
<div class='clearfix'>
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">65.85% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Statements</span>
|
||||
<span class='fraction'>27/41</span>
|
||||
<span class='fraction'>61/61</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">56% </span>
|
||||
<span class="strong">97.22% </span>
|
||||
<span class="quiet">Branches</span>
|
||||
<span class='fraction'>14/25</span>
|
||||
<span class='fraction'>35/36</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">85.71% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Functions</span>
|
||||
<span class='fraction'>6/7</span>
|
||||
<span class='fraction'>11/11</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='fl pad1y space-right2'>
|
||||
<span class="strong">72.22% </span>
|
||||
<span class="strong">100% </span>
|
||||
<span class="quiet">Lines</span>
|
||||
<span class='fraction'>26/36</span>
|
||||
<span class='fraction'>55/55</span>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class='status-line medium'></div>
|
||||
<div class='status-line high'></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>
|
||||
@@ -131,74 +131,198 @@
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-yes">6x</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-yes">8x</span>
|
||||
<span class="cline-any cline-yes">27x</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-yes">39x</span>
|
||||
<span class="cline-any cline-yes">17x</span>
|
||||
<span class="cline-any cline-yes">17x</span>
|
||||
<span class="cline-any cline-yes">15x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">17x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">17x</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-yes">30x</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-yes">6x</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-yes">9x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">9x</span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-yes">2x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</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-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">1x</span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<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-yes">6x</span>
|
||||
<span class="cline-any cline-yes">3x</span>
|
||||
<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-neutral"> </span>
|
||||
<span class="cline-any cline-no"> </span>
|
||||
<span class="cline-any cline-yes">9x</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-yes">6x</span>
|
||||
<span class="cline-any cline-yes">6x</span>
|
||||
<span class="cline-any cline-yes">12x</span>
|
||||
<span class="cline-any cline-yes">5x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">7x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">6x</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-no"> </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-yes">1x</span>
|
||||
<span class="cline-any cline-yes">154x</span>
|
||||
<span class="cline-any cline-yes">92x</span>
|
||||
<span class="cline-any cline-yes">92x</span>
|
||||
<span class="cline-any cline-yes">88x</span>
|
||||
<span class="cline-any cline-yes">88x</span>
|
||||
<span class="cline-any cline-yes">88x</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-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</span>
|
||||
<span class="cline-any cline-yes">16x</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-yes">8x</span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">8x</span>
|
||||
<span class="cline-any cline-yes">10x</span>
|
||||
<span class="cline-any cline-yes">10x</span>
|
||||
<span class="cline-any cline-yes">10x</span>
|
||||
<span class="cline-any cline-yes">10x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-yes">8x</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-yes">127x</span>
|
||||
<span class="cline-any cline-yes">127x</span>
|
||||
<span class="cline-any cline-yes">127x</span>
|
||||
<span class="cline-any cline-yes">127x</span>
|
||||
<span class="cline-any cline-yes">127x</span>
|
||||
<span class="cline-any cline-yes">57x</span>
|
||||
<span class="cline-any cline-neutral"> </span>
|
||||
<span class="cline-any cline-neutral"> </span></td><td class="text"><pre class="prettyprint lang-js">// Wikilink placeholder tokens for markdown round-trip
|
||||
const WL_START = '\u2039WIKILINK:'
|
||||
const WL_END = '\u203A'
|
||||
@@ -206,46 +330,90 @@ const WL_RE = new RegExp(`${WL_START.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}
|
||||
|
||||
/** Pre-process markdown: replace [[target]] with placeholder tokens */
|
||||
export function preProcessWikilinks(md: string): string {
|
||||
return md.replace(/\[\[([^\]]+)\]\]/g, <span class="fstat-no" title="function not covered" >(_</span>m, target) => <span class="cstat-no" title="statement not covered" >`${WL_START}${target}${WL_END}`)</span>
|
||||
return md.replace(/\[\[([^\]]+)\]\]/g, (_m, target) => `${WL_START}${target}${WL_END}`)
|
||||
}
|
||||
|
||||
/** Walk blocks and replace placeholder text with wikilink inline content */
|
||||
export function injectWikilinks(blocks: any[]): any[] {
|
||||
return blocks.map(block => {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (block.content && Array.isArray(block.content)) {
|
||||
block.content = expandWikilinksInContent(block.content)
|
||||
// Minimal shape of a BlockNote block for wikilink processing
|
||||
interface BlockLike {
|
||||
content?: InlineItem[]
|
||||
children?: BlockLike[]
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface InlineItem {
|
||||
type: string
|
||||
text?: string
|
||||
props?: Record<string, string>
|
||||
content?: unknown
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
type ContentTransform = (content: InlineItem[]) => InlineItem[]
|
||||
|
||||
/** Walk blocks recursively, applying a transform to each block's inline content */
|
||||
function walkBlocks(blocks: unknown[], transform: ContentTransform, clone = false): unknown[] {
|
||||
return (blocks as BlockLike[]).map(block => {
|
||||
const b = clone ? { ...block } : block
|
||||
if (b.content && Array.isArray(b.content)) {
|
||||
b.content = transform(b.content)
|
||||
}
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (block.children && Array.isArray(block.children)) {
|
||||
block.children = injectWikilinks(block.children)
|
||||
if (b.children && Array.isArray(b.children)) {
|
||||
b.children = walkBlocks(b.children, transform, clone) as BlockLike[]
|
||||
}
|
||||
return block
|
||||
return b
|
||||
})
|
||||
}
|
||||
|
||||
function expandWikilinksInContent(content: any[]): any[] {
|
||||
const result: any[] = []
|
||||
/** Walk blocks and replace placeholder text with wikilink inline content */
|
||||
export function injectWikilinks(blocks: unknown[]): unknown[] {
|
||||
return walkBlocks(blocks, expandWikilinksInContent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep-clone blocks and convert wikilink inline content back to [[target]] text.
|
||||
* This is the reverse of injectWikilinks — used before blocksToMarkdownLossy
|
||||
* so that wikilinks survive the markdown round-trip.
|
||||
*/
|
||||
export function restoreWikilinksInBlocks(blocks: unknown[]): unknown[] {
|
||||
return walkBlocks(blocks, collapseWikilinksInContent, true)
|
||||
}
|
||||
|
||||
function expandWikilinksInContent(content: InlineItem[]): InlineItem[] {
|
||||
const result: InlineItem[] = []
|
||||
for (const item of content) {
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (item.type !== 'text' || typeof item.text !== 'string' || !item.text.includes(WL_START)) {
|
||||
if (item.type !== 'text' || typeof item.text !== 'string' || !item.text.includes(WL_START)) {
|
||||
result.push(item)
|
||||
continue
|
||||
}
|
||||
const text = <span class="cstat-no" title="statement not covered" >item.text as string</span>
|
||||
let lastIndex = <span class="cstat-no" title="statement not covered" >0</span>
|
||||
<span class="cstat-no" title="statement not covered" > WL_RE.lastIndex = 0</span>
|
||||
const text = item.text as string
|
||||
let lastIndex = 0
|
||||
WL_RE.lastIndex = 0
|
||||
let match
|
||||
<span class="cstat-no" title="statement not covered" > while ((match = WL_RE.exec(text)) !== null) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > if (match.index > lastIndex) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > result.push({ ...item, text: text.slice(lastIndex, match.index) })</span>
|
||||
while ((match = WL_RE.exec(text)) !== null) {
|
||||
if (match.index > lastIndex) {
|
||||
result.push({ ...item, text: text.slice(lastIndex, match.index) })
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > result.push({</span>
|
||||
result.push({
|
||||
type: 'wikilink',
|
||||
props: { target: match[1] },
|
||||
content: undefined,
|
||||
})
|
||||
<span class="cstat-no" title="statement not covered" > lastIndex = match.index + match[0].length</span>
|
||||
lastIndex = match.index + match[0].length
|
||||
}
|
||||
<span class="cstat-no" title="statement not covered" > if (lastIndex < text.length) {</span>
|
||||
<span class="cstat-no" title="statement not covered" > result.push({ ...item, text: text.slice(lastIndex) })</span>
|
||||
if (lastIndex < text.length) {
|
||||
result.push({ ...item, text: text.slice(lastIndex) })
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
function collapseWikilinksInContent(content: InlineItem[]): InlineItem[] {
|
||||
const result: InlineItem[] = []
|
||||
for (const item of content) {
|
||||
if (item.type === 'wikilink' && item.props?.target) {
|
||||
result.push({ type: 'text', text: `[[${item.props.target}]]` })
|
||||
} else {
|
||||
result.push(item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
@@ -253,18 +421,36 @@ function expandWikilinksInContent(content: any[]): any[] {
|
||||
|
||||
/** Strip YAML frontmatter from markdown, returning [frontmatter, body] */
|
||||
export function splitFrontmatter(content: string): [string, string] {
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (!content.startsWith('---')) <span class="cstat-no" title="statement not covered" >return ['', content]</span>
|
||||
if (!content.startsWith('---')) return ['', content]
|
||||
const end = content.indexOf('\n---', 3)
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (end === -1) <span class="cstat-no" title="statement not covered" >return ['', content]</span>
|
||||
if (end === -1) return ['', content]
|
||||
let to = end + 4
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (content[to] === '\n') to++
|
||||
if (content[to] === '\n') to++
|
||||
return [content.slice(0, to), content.slice(to)]
|
||||
}
|
||||
|
||||
/** Extract all outgoing wikilink targets from content.
|
||||
* Finds [[target]] and [[target|display]] patterns, returning just the target part.
|
||||
* Returns a sorted, deduplicated array. */
|
||||
export function extractOutgoingLinks(content: string): string[] {
|
||||
const links: string[] = []
|
||||
const re = /\[\[([^\]]+)\]\]/g
|
||||
let match
|
||||
while ((match = re.exec(content)) !== null) {
|
||||
const inner = match[1]
|
||||
const pipeIdx = inner.indexOf('|')
|
||||
const target = pipeIdx !== -1 ? inner.slice(0, pipeIdx) : inner
|
||||
<span class="missing-if-branch" title="else path not taken" >E</span>if (target) links.push(target)
|
||||
}
|
||||
return [...new Set(links)].sort()
|
||||
}
|
||||
|
||||
export function countWords(content: string): number {
|
||||
const [, body] = splitFrontmatter(content)
|
||||
const text = body.replace(/[#*_\[\]`>~\-|]/g, '').trim()
|
||||
<span class="missing-if-branch" title="if path not taken" >I</span>if (!text) <span class="cstat-no" title="statement not covered" >return 0</span>
|
||||
const withoutTitle = body.replace(/^\s*# [^\n]+\n?/, '')
|
||||
const withoutWikilinks = withoutTitle.replace(/\[\[[^\]]*\]\]/g, '')
|
||||
const text = withoutWikilinks.replace(/[#*_[\]`>~\-|]/g, '').trim()
|
||||
if (!text) return 0
|
||||
return text.split(/\s+/).filter(Boolean).length
|
||||
}
|
||||
</pre></td></tr></table></pre>
|
||||
@@ -274,7 +460,7 @@ export function countWords(content: string): number {
|
||||
<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>
|
||||
at 2026-02-22T09:10:55.587Z
|
||||
at 2026-02-25T20:19:25.575Z
|
||||
</div>
|
||||
<script src="../../prettify.js"></script>
|
||||
<script>
|
||||
|
||||
1
design/search-panel-unify.pen
Normal file
1
design/search-panel-unify.pen
Normal file
File diff suppressed because one or more lines are too long
@@ -35,6 +35,7 @@ const MOCK_ENTRIES: VaultEntry[] = [
|
||||
icon: null,
|
||||
color: null,
|
||||
order: null,
|
||||
outgoingLinks: [],
|
||||
},
|
||||
{
|
||||
path: '/vault/event/retreat.md',
|
||||
@@ -58,6 +59,7 @@ const MOCK_ENTRIES: VaultEntry[] = [
|
||||
icon: null,
|
||||
color: null,
|
||||
order: null,
|
||||
outgoingLinks: [],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -68,31 +70,39 @@ describe('SearchPanel', () => {
|
||||
|
||||
it('renders nothing when closed', () => {
|
||||
const { container } = render(
|
||||
<SearchPanel open={false} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />
|
||||
<SearchPanel open={false} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
expect(container.innerHTML).toBe('')
|
||||
})
|
||||
|
||||
it('renders search input when open', () => {
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
expect(screen.getByPlaceholderText('Search in all notes...')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows empty state hint when no query', () => {
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
expect(screen.getByText('Search across all note contents')).toBeInTheDocument()
|
||||
expect(screen.getByText('Enter to open · Esc to close')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('has no keyword/semantic toggle', () => {
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
expect(screen.queryByText('Keyword')).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('Semantic')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('calls onClose when clicking overlay', () => {
|
||||
const onClose = vi.fn()
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={onClose} />
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={onClose} />,
|
||||
)
|
||||
// Click the overlay (outermost div)
|
||||
const overlay = screen.getByPlaceholderText('Search in all notes...').closest('.fixed')!
|
||||
fireEvent.click(overlay)
|
||||
expect(onClose).toHaveBeenCalled()
|
||||
@@ -101,13 +111,13 @@ describe('SearchPanel', () => {
|
||||
it('calls onClose on Escape key', () => {
|
||||
const onClose = vi.fn()
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={onClose} />
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={onClose} />,
|
||||
)
|
||||
fireEvent.keyDown(window, { key: 'Escape' })
|
||||
expect(onClose).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('performs search on query input with debounce', async () => {
|
||||
it('performs unified search with keyword then hybrid', async () => {
|
||||
mockInvokeFn.mockResolvedValue({
|
||||
results: [
|
||||
{ title: 'How to Design AI-first APIs', path: '/vault/essay/ai-apis.md', snippet: '...designing APIs for AI...', score: 0.87, note_type: 'Essay' },
|
||||
@@ -116,12 +126,13 @@ describe('SearchPanel', () => {
|
||||
})
|
||||
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
|
||||
const input = screen.getByPlaceholderText('Search in all notes...')
|
||||
fireEvent.change(input, { target: { value: 'api design' } })
|
||||
|
||||
// Should call keyword search first
|
||||
await waitFor(() => {
|
||||
expect(mockInvokeFn).toHaveBeenCalledWith('search_vault', {
|
||||
vaultPath: '/vault',
|
||||
@@ -131,16 +142,27 @@ describe('SearchPanel', () => {
|
||||
})
|
||||
})
|
||||
|
||||
// Results should appear
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('How to Design AI-first APIs')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Should also call hybrid search
|
||||
await waitFor(() => {
|
||||
expect(mockInvokeFn).toHaveBeenCalledWith('search_vault', {
|
||||
vaultPath: '/vault',
|
||||
query: 'api design',
|
||||
mode: 'hybrid',
|
||||
limit: 20,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('shows no results message when search returns empty', async () => {
|
||||
mockInvokeFn.mockResolvedValue({ results: [], elapsed_ms: 10 })
|
||||
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
|
||||
const input = screen.getByPlaceholderText('Search in all notes...')
|
||||
@@ -161,7 +183,7 @@ describe('SearchPanel', () => {
|
||||
})
|
||||
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
|
||||
const input = screen.getByPlaceholderText('Search in all notes...')
|
||||
@@ -171,10 +193,8 @@ describe('SearchPanel', () => {
|
||||
expect(screen.getByText('Result One')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Arrow down should highlight second result
|
||||
fireEvent.keyDown(window, { key: 'ArrowDown' })
|
||||
|
||||
// The second item should now have bg-accent class
|
||||
await waitFor(() => {
|
||||
const resultTwo = screen.getByText('Result Two').closest('[class*="cursor-pointer"]')!
|
||||
expect(resultTwo.className).toContain('bg-accent')
|
||||
@@ -192,7 +212,7 @@ describe('SearchPanel', () => {
|
||||
const onSelectNote = vi.fn()
|
||||
const onClose = vi.fn()
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={onSelectNote} onClose={onClose} />
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={onSelectNote} onClose={onClose} />,
|
||||
)
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText('Search in all notes...'), { target: { value: 'api' } })
|
||||
@@ -201,7 +221,6 @@ describe('SearchPanel', () => {
|
||||
expect(screen.getByText('How to Design AI-first APIs')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Wait for the effect to re-register with new results before firing Enter
|
||||
fireEvent.keyDown(window, { key: 'Enter' })
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -210,32 +229,6 @@ describe('SearchPanel', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('toggles search mode with Tab key', async () => {
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />
|
||||
)
|
||||
|
||||
// Initial mode is keyword — the keyword button should be styled active
|
||||
const keywordBtn = screen.getByText('Keyword')
|
||||
expect(keywordBtn.className).toContain('bg-secondary')
|
||||
|
||||
// Tab toggles to semantic
|
||||
fireEvent.keyDown(window, { key: 'Tab' })
|
||||
|
||||
const semanticBtn = screen.getByText('Semantic')
|
||||
expect(semanticBtn.className).toContain('bg-secondary')
|
||||
})
|
||||
|
||||
it('toggles mode via button click', () => {
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByText('Semantic'))
|
||||
expect(screen.getByText('Semantic').className).toContain('bg-secondary')
|
||||
expect(screen.getByText('Keyword').className).not.toContain('bg-secondary')
|
||||
})
|
||||
|
||||
it('shows result count and elapsed time', async () => {
|
||||
mockInvokeFn.mockResolvedValue({
|
||||
results: [
|
||||
@@ -245,7 +238,7 @@ describe('SearchPanel', () => {
|
||||
})
|
||||
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText('Search in all notes...'), { target: { value: 'test' } })
|
||||
@@ -265,7 +258,7 @@ describe('SearchPanel', () => {
|
||||
})
|
||||
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText('Search in all notes...'), { target: { value: 'api' } })
|
||||
@@ -274,4 +267,105 @@ describe('SearchPanel', () => {
|
||||
expect(screen.getByText('Essay')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('shows loading spinner while searching', async () => {
|
||||
const resolvers: ((v: unknown) => void)[] = []
|
||||
mockInvokeFn.mockImplementation(
|
||||
() => new Promise(resolve => { resolvers.push(resolve) }),
|
||||
)
|
||||
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText('Search in all notes...'), { target: { value: 'test' } })
|
||||
|
||||
// Spinner appears when keyword search starts (after debounce)
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('search-spinner')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Resolve keyword search
|
||||
resolvers[0]({
|
||||
results: [{ title: 'Result', path: '/vault/essay/ai-apis.md', snippet: '', score: 0.9, note_type: null }],
|
||||
elapsed_ms: 30,
|
||||
})
|
||||
|
||||
// Keyword results appear, spinner still visible (hybrid in progress)
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Result')).toBeInTheDocument()
|
||||
expect(screen.getByTestId('search-spinner')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Wait for hybrid call then resolve it
|
||||
await waitFor(() => { expect(resolvers).toHaveLength(2) })
|
||||
resolvers[1]({
|
||||
results: [{ title: 'Result', path: '/vault/essay/ai-apis.md', snippet: '', score: 0.9, note_type: null }],
|
||||
elapsed_ms: 150,
|
||||
})
|
||||
|
||||
// Spinner disappears after hybrid completes
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('search-spinner')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('discards stale results when query changes rapidly', async () => {
|
||||
mockInvokeFn.mockImplementation(async (_cmd: string, args?: Record<string, unknown>) => {
|
||||
const q = (args as Record<string, string>)?.query
|
||||
if (q === 'second') {
|
||||
return {
|
||||
results: [{ title: 'Second Result', path: '/vault/event/retreat.md', snippet: '', score: 0.9, note_type: null }],
|
||||
elapsed_ms: 30,
|
||||
}
|
||||
}
|
||||
return { results: [], elapsed_ms: 0 }
|
||||
})
|
||||
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
|
||||
const input = screen.getByPlaceholderText('Search in all notes...')
|
||||
// Type first query, then immediately change to second (within debounce)
|
||||
fireEvent.change(input, { target: { value: 'first' } })
|
||||
fireEvent.change(input, { target: { value: 'second' } })
|
||||
|
||||
// Only second query results should appear
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Second Result')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps keyword results when hybrid search fails', async () => {
|
||||
mockInvokeFn.mockImplementation(async (_cmd: string, args?: Record<string, unknown>) => {
|
||||
const mode = (args as Record<string, string>)?.mode
|
||||
if (mode === 'keyword') {
|
||||
return {
|
||||
results: [{ title: 'Keyword Only', path: '/vault/essay/ai-apis.md', snippet: '', score: 0.9, note_type: null }],
|
||||
elapsed_ms: 30,
|
||||
}
|
||||
}
|
||||
throw new Error('qmd unavailable')
|
||||
})
|
||||
|
||||
render(
|
||||
<SearchPanel open={true} vaultPath="/vault" entries={MOCK_ENTRIES} onSelectNote={vi.fn()} onClose={vi.fn()} />,
|
||||
)
|
||||
|
||||
fireEvent.change(screen.getByPlaceholderText('Search in all notes...'), { target: { value: 'test' } })
|
||||
|
||||
// Keyword results appear
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Keyword Only')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Spinner disappears after hybrid fails
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByTestId('search-spinner')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Keyword results remain
|
||||
expect(screen.getByText('Keyword Only')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,26 +1,8 @@
|
||||
import { useState, useRef, useEffect, useCallback } from 'react'
|
||||
import { useRef, useEffect, useCallback } from 'react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import type { SearchMode, SearchResult, VaultEntry } from '../types'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { isTauri, mockInvoke } from '../mock-tauri'
|
||||
|
||||
interface SearchResultData {
|
||||
title: string
|
||||
path: string
|
||||
snippet: string
|
||||
score: number
|
||||
note_type: string | null
|
||||
}
|
||||
|
||||
interface SearchResponseData {
|
||||
results: SearchResultData[]
|
||||
elapsed_ms: number
|
||||
}
|
||||
|
||||
function searchCall(args: Record<string, unknown>): Promise<SearchResponseData> {
|
||||
return isTauri() ? invoke<SearchResponseData>('search_vault', args) : mockInvoke<SearchResponseData>('search_vault', args)
|
||||
}
|
||||
import type { SearchResult, VaultEntry } from '../types'
|
||||
import { useUnifiedSearch } from '../hooks/useUnifiedSearch'
|
||||
|
||||
interface SearchPanelProps {
|
||||
open: boolean
|
||||
@@ -31,76 +13,17 @@ interface SearchPanelProps {
|
||||
}
|
||||
|
||||
export function SearchPanel({ open, vaultPath, entries, onSelectNote, onClose }: SearchPanelProps) {
|
||||
const [query, setQuery] = useState('')
|
||||
const [mode, setMode] = useState<SearchMode>('keyword')
|
||||
const [results, setResults] = useState<SearchResult[]>([])
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [elapsedMs, setElapsedMs] = useState<number | null>(null)
|
||||
const [searchError, setSearchError] = useState<string | null>(null)
|
||||
const {
|
||||
query, setQuery, results, selectedIndex, setSelectedIndex, loading, elapsedMs,
|
||||
} = useUnifiedSearch(vaultPath, open)
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const listRef = useRef<HTMLDivElement>(null)
|
||||
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setQuery('')
|
||||
setResults([])
|
||||
setSelectedIndex(0)
|
||||
setElapsedMs(null)
|
||||
setSearchError(null)
|
||||
setTimeout(() => inputRef.current?.focus(), 50)
|
||||
}
|
||||
if (open) setTimeout(() => inputRef.current?.focus(), 50)
|
||||
}, [open])
|
||||
|
||||
const performSearch = useCallback(async (q: string, m: SearchMode) => {
|
||||
if (!q.trim()) {
|
||||
setResults([])
|
||||
setElapsedMs(null)
|
||||
setSearchError(null)
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
setSearchError(null)
|
||||
try {
|
||||
const response = await searchCall({
|
||||
vaultPath,
|
||||
query: q,
|
||||
mode: m,
|
||||
limit: 20,
|
||||
})
|
||||
const mapped = response.results.map((r: SearchResultData) => ({
|
||||
title: r.title,
|
||||
path: r.path,
|
||||
snippet: r.snippet,
|
||||
score: r.score,
|
||||
noteType: r.note_type,
|
||||
}))
|
||||
setResults(mapped)
|
||||
setElapsedMs(response.elapsed_ms)
|
||||
setSelectedIndex(0)
|
||||
} catch (err) {
|
||||
setSearchError(String(err))
|
||||
setResults([])
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [vaultPath])
|
||||
|
||||
useEffect(() => {
|
||||
if (debounceRef.current) clearTimeout(debounceRef.current)
|
||||
if (!query.trim()) {
|
||||
setResults([])
|
||||
setElapsedMs(null)
|
||||
return
|
||||
}
|
||||
debounceRef.current = setTimeout(() => {
|
||||
performSearch(query, mode)
|
||||
}, 200)
|
||||
return () => { if (debounceRef.current) clearTimeout(debounceRef.current) }
|
||||
}, [query, mode, performSearch])
|
||||
|
||||
useEffect(() => {
|
||||
if (!listRef.current) return
|
||||
const selected = listRef.current.children[selectedIndex] as HTMLElement | undefined
|
||||
@@ -129,17 +52,12 @@ export function SearchPanel({ open, vaultPath, entries, onSelectNote, onClose }:
|
||||
setSelectedIndex(i => Math.max(i - 1, 0))
|
||||
} else if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
if (results[selectedIndex]) {
|
||||
handleSelect(results[selectedIndex])
|
||||
}
|
||||
} else if (e.key === 'Tab') {
|
||||
e.preventDefault()
|
||||
setMode(m => m === 'keyword' ? 'semantic' : 'keyword')
|
||||
if (results[selectedIndex]) handleSelect(results[selectedIndex])
|
||||
}
|
||||
}
|
||||
window.addEventListener('keydown', handleKey)
|
||||
return () => window.removeEventListener('keydown', handleKey)
|
||||
}, [open, results, selectedIndex, handleSelect, onClose])
|
||||
}, [open, results, selectedIndex, handleSelect, setSelectedIndex, onClose])
|
||||
|
||||
if (!open) return null
|
||||
|
||||
@@ -154,120 +72,145 @@ export function SearchPanel({ open, vaultPath, entries, onSelectNote, onClose }:
|
||||
className="flex w-[540px] max-w-[90vw] max-h-[480px] flex-col self-start overflow-hidden rounded-xl border border-[var(--border-dialog)] bg-popover shadow-[0_8px_32px_var(--shadow-dialog)]"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
{/* Search input row */}
|
||||
<div className="flex items-center gap-3 border-b border-border px-4 py-3">
|
||||
<svg className="h-4 w-4 shrink-0 text-muted-foreground" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path d="m21 21-4.35-4.35" />
|
||||
</svg>
|
||||
<input
|
||||
ref={inputRef}
|
||||
className="flex-1 bg-transparent text-[15px] text-foreground outline-none placeholder:text-muted-foreground"
|
||||
type="text"
|
||||
placeholder="Search in all notes..."
|
||||
value={query}
|
||||
onChange={e => setQuery(e.target.value)}
|
||||
/>
|
||||
<div className="flex gap-1">
|
||||
<button
|
||||
className={cn(
|
||||
"rounded-md px-2 py-1 text-[11px] font-medium transition-colors",
|
||||
mode === 'keyword'
|
||||
? "bg-secondary text-foreground"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
)}
|
||||
onClick={() => setMode('keyword')}
|
||||
>
|
||||
Keyword
|
||||
</button>
|
||||
<button
|
||||
className={cn(
|
||||
"rounded-md px-2 py-1 text-[11px] font-medium transition-colors",
|
||||
mode === 'semantic'
|
||||
? "bg-secondary text-foreground"
|
||||
: "text-muted-foreground hover:text-foreground"
|
||||
)}
|
||||
onClick={() => setMode('semantic')}
|
||||
>
|
||||
Semantic
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content area */}
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{loading && (
|
||||
<div className="px-4 py-8 text-center text-[13px] text-muted-foreground">
|
||||
Searching...
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && !query.trim() && (
|
||||
<div className="px-4 py-8 text-center">
|
||||
<p className="text-[13px] text-muted-foreground">Search across all note contents</p>
|
||||
<p className="mt-1 text-[11px] text-muted-foreground/60">
|
||||
Tab to toggle keyword/semantic · Enter to open · Esc to close
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && query.trim() && results.length === 0 && !searchError && (
|
||||
<div className="px-4 py-8 text-center">
|
||||
<p className="text-[13px] text-muted-foreground">No results found</p>
|
||||
<p className="mt-1 text-[11px] text-muted-foreground/60">
|
||||
Try different keywords or switch to semantic search
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{searchError && (
|
||||
<div className="px-4 py-8 text-center">
|
||||
<p className="text-[13px] text-destructive">Search error</p>
|
||||
<p className="mt-1 text-[11px] text-muted-foreground">{searchError}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && results.length > 0 && (
|
||||
<>
|
||||
<div className="border-b border-border/50 px-4 py-1.5">
|
||||
<span className="text-[11px] text-muted-foreground">
|
||||
{results.length} result{results.length !== 1 ? 's' : ''}{elapsedMs !== null ? ` · ${elapsedMs}ms` : ''}
|
||||
</span>
|
||||
</div>
|
||||
<div ref={listRef}>
|
||||
{results.map((result, i) => {
|
||||
const noteType = entryTypeMap.get(result.path) ?? result.noteType
|
||||
return (
|
||||
<div
|
||||
key={result.path}
|
||||
className={cn(
|
||||
"cursor-pointer px-4 py-2.5 transition-colors",
|
||||
i === selectedIndex ? "bg-accent" : "hover:bg-secondary"
|
||||
)}
|
||||
onClick={() => handleSelect(result)}
|
||||
onMouseEnter={() => setSelectedIndex(i)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-[13px] font-medium text-foreground">{result.title}</span>
|
||||
{noteType && (
|
||||
<Badge variant="secondary" className="text-[10px]">
|
||||
{noteType}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{result.snippet && (
|
||||
<p className="mt-1 line-clamp-2 text-[12px] leading-relaxed text-muted-foreground">
|
||||
{result.snippet}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<SearchInput
|
||||
ref={inputRef}
|
||||
query={query}
|
||||
loading={loading}
|
||||
onChange={setQuery}
|
||||
/>
|
||||
<SearchContent
|
||||
query={query}
|
||||
results={results}
|
||||
selectedIndex={selectedIndex}
|
||||
loading={loading}
|
||||
elapsedMs={elapsedMs}
|
||||
entryTypeMap={entryTypeMap}
|
||||
listRef={listRef}
|
||||
onSelect={handleSelect}
|
||||
onHover={setSelectedIndex}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
import { forwardRef } from 'react'
|
||||
|
||||
interface SearchInputProps {
|
||||
query: string
|
||||
loading: boolean
|
||||
onChange: (value: string) => void
|
||||
}
|
||||
|
||||
const SearchInput = forwardRef<HTMLInputElement, SearchInputProps>(
|
||||
function SearchInput({ query, loading, onChange }, ref) {
|
||||
return (
|
||||
<div className="flex items-center gap-3 border-b border-border px-4 py-3">
|
||||
<svg className="h-4 w-4 shrink-0 text-muted-foreground" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||
<circle cx="11" cy="11" r="8" />
|
||||
<path d="m21 21-4.35-4.35" />
|
||||
</svg>
|
||||
<input
|
||||
ref={ref}
|
||||
className="flex-1 bg-transparent text-[15px] text-foreground outline-none placeholder:text-muted-foreground"
|
||||
type="text"
|
||||
placeholder="Search in all notes..."
|
||||
value={query}
|
||||
onChange={e => onChange(e.target.value)}
|
||||
/>
|
||||
{loading && (
|
||||
<svg
|
||||
className="h-4 w-4 shrink-0 animate-spin text-muted-foreground"
|
||||
data-testid="search-spinner"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
interface SearchContentProps {
|
||||
query: string
|
||||
results: SearchResult[]
|
||||
selectedIndex: number
|
||||
loading: boolean
|
||||
elapsedMs: number | null
|
||||
entryTypeMap: Map<string, string | null>
|
||||
listRef: React.RefObject<HTMLDivElement | null>
|
||||
onSelect: (result: SearchResult) => void
|
||||
onHover: (index: number) => void
|
||||
}
|
||||
|
||||
function SearchContent({
|
||||
query, results, selectedIndex, loading, elapsedMs, entryTypeMap, listRef, onSelect, onHover,
|
||||
}: SearchContentProps) {
|
||||
return (
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{!query.trim() && (
|
||||
<div className="px-4 py-8 text-center">
|
||||
<p className="text-[13px] text-muted-foreground">Search across all note contents</p>
|
||||
<p className="mt-1 text-[11px] text-muted-foreground/60">
|
||||
Enter to open · Esc to close
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{query.trim() && results.length === 0 && loading && (
|
||||
<div className="px-4 py-8 text-center text-[13px] text-muted-foreground">
|
||||
Searching...
|
||||
</div>
|
||||
)}
|
||||
|
||||
{query.trim() && results.length === 0 && !loading && (
|
||||
<div className="px-4 py-8 text-center">
|
||||
<p className="text-[13px] text-muted-foreground">No results found</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{results.length > 0 && (
|
||||
<>
|
||||
<div className="border-b border-border/50 px-4 py-1.5">
|
||||
<span className="text-[11px] text-muted-foreground">
|
||||
{results.length} result{results.length !== 1 ? 's' : ''}{elapsedMs !== null ? ` · ${elapsedMs}ms` : ''}
|
||||
</span>
|
||||
</div>
|
||||
<div ref={listRef}>
|
||||
{results.map((result, i) => {
|
||||
const noteType = entryTypeMap.get(result.path) ?? result.noteType
|
||||
return (
|
||||
<div
|
||||
key={result.path}
|
||||
className={cn(
|
||||
"cursor-pointer px-4 py-2.5 transition-colors",
|
||||
i === selectedIndex ? "bg-accent" : "hover:bg-secondary",
|
||||
)}
|
||||
onClick={() => onSelect(result)}
|
||||
onMouseEnter={() => onHover(i)}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-[13px] font-medium text-foreground">{result.title}</span>
|
||||
{noteType && (
|
||||
<Badge variant="secondary" className="text-[10px]">
|
||||
{noteType}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{result.snippet && (
|
||||
<p className="mt-1 line-clamp-2 text-[12px] leading-relaxed text-muted-foreground">
|
||||
{result.snippet}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
124
src/hooks/useUnifiedSearch.ts
Normal file
124
src/hooks/useUnifiedSearch.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
import { useState, useRef, useEffect, useCallback } from 'react'
|
||||
import type { SearchResult } from '../types'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { isTauri, mockInvoke } from '../mock-tauri'
|
||||
|
||||
interface SearchResultData {
|
||||
title: string
|
||||
path: string
|
||||
snippet: string
|
||||
score: number
|
||||
note_type: string | null
|
||||
}
|
||||
|
||||
interface SearchResponseData {
|
||||
results: SearchResultData[]
|
||||
elapsed_ms: number
|
||||
}
|
||||
|
||||
const DEBOUNCE_MS = 300
|
||||
const HYBRID_TIMEOUT_MS = 5000
|
||||
|
||||
function searchCall(args: Record<string, unknown>): Promise<SearchResponseData> {
|
||||
return isTauri()
|
||||
? invoke<SearchResponseData>('search_vault', args)
|
||||
: mockInvoke<SearchResponseData>('search_vault', args)
|
||||
}
|
||||
|
||||
function searchWithTimeout(args: Record<string, unknown>, ms: number): Promise<SearchResponseData> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => reject(new Error('Search timeout')), ms)
|
||||
searchCall(args).then(
|
||||
result => { clearTimeout(timer); resolve(result) },
|
||||
err => { clearTimeout(timer); reject(err) },
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function mapResults(raw: SearchResultData[]): SearchResult[] {
|
||||
return raw.map(r => ({
|
||||
title: r.title,
|
||||
path: r.path,
|
||||
snippet: r.snippet,
|
||||
score: r.score,
|
||||
noteType: r.note_type,
|
||||
}))
|
||||
}
|
||||
|
||||
export function useUnifiedSearch(vaultPath: string, active: boolean) {
|
||||
const [query, setQuery] = useState('')
|
||||
const [results, setResults] = useState<SearchResult[]>([])
|
||||
const [selectedIndex, setSelectedIndex] = useState(0)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [elapsedMs, setElapsedMs] = useState<number | null>(null)
|
||||
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const searchGenRef = useRef(0)
|
||||
|
||||
const reset = useCallback(() => {
|
||||
setQuery('')
|
||||
setResults([])
|
||||
setSelectedIndex(0)
|
||||
setElapsedMs(null)
|
||||
setLoading(false)
|
||||
searchGenRef.current++
|
||||
}, [])
|
||||
|
||||
useEffect(() => { if (active) reset() }, [active, reset])
|
||||
|
||||
const performSearch = useCallback(async (q: string) => {
|
||||
if (!q.trim()) {
|
||||
setResults([])
|
||||
setElapsedMs(null)
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
searchGenRef.current++
|
||||
const gen = searchGenRef.current
|
||||
setLoading(true)
|
||||
|
||||
// Phase 1: Keyword search (fast)
|
||||
try {
|
||||
const response = await searchCall({ vaultPath, query: q, mode: 'keyword', limit: 20 })
|
||||
if (gen !== searchGenRef.current) return
|
||||
setResults(mapResults(response.results))
|
||||
setElapsedMs(response.elapsed_ms)
|
||||
setSelectedIndex(0)
|
||||
} catch {
|
||||
if (gen !== searchGenRef.current) return
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
// Phase 2: Hybrid search — augments keyword results with semantic
|
||||
try {
|
||||
const response = await searchWithTimeout(
|
||||
{ vaultPath, query: q, mode: 'hybrid', limit: 20 },
|
||||
HYBRID_TIMEOUT_MS,
|
||||
)
|
||||
if (gen !== searchGenRef.current) return
|
||||
setResults(mapResults(response.results))
|
||||
setElapsedMs(response.elapsed_ms)
|
||||
setSelectedIndex(prev => Math.min(prev, Math.max(response.results.length - 1, 0)))
|
||||
} catch {
|
||||
// Hybrid failed or timed out — keyword results remain visible
|
||||
} finally {
|
||||
if (gen === searchGenRef.current) setLoading(false)
|
||||
}
|
||||
}, [vaultPath])
|
||||
|
||||
useEffect(() => {
|
||||
if (debounceRef.current) clearTimeout(debounceRef.current)
|
||||
if (!query.trim()) {
|
||||
setResults([])
|
||||
setElapsedMs(null)
|
||||
searchGenRef.current++
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
debounceRef.current = setTimeout(() => performSearch(query), DEBOUNCE_MS)
|
||||
return () => { if (debounceRef.current) clearTimeout(debounceRef.current) }
|
||||
}, [query, performSearch])
|
||||
|
||||
return { query, setQuery, results, selectedIndex, setSelectedIndex, loading, elapsedMs }
|
||||
}
|
||||
Reference in New Issue
Block a user