aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.thicket/tickets.jsonl3
-rw-r--r--frontend/coverage/base.css384
-rw-r--r--frontend/coverage/block-navigation.js133
-rw-r--r--frontend/coverage/clover.xml472
-rw-r--r--frontend/coverage/coverage-final.json2252
-rw-r--r--frontend/coverage/index.html238
-rw-r--r--frontend/coverage/prettify.css102
-rw-r--r--frontend/coverage/prettify.js939
-rw-r--r--frontend/coverage/sorter.js379
-rw-r--r--frontend/coverage/src/App.css.html273
-rw-r--r--frontend/coverage/src/App.tsx.html205
-rw-r--r--frontend/coverage/src/components/FeedItem.css.html298
-rw-r--r--frontend/coverage/src/components/FeedItem.tsx.html380
-rw-r--r--frontend/coverage/src/components/FeedItems.css.html183
-rw-r--r--frontend/coverage/src/components/FeedItems.tsx.html597
-rw-r--r--frontend/coverage/src/components/FeedList.css.html495
-rw-r--r--frontend/coverage/src/components/FeedList.tsx.html620
-rw-r--r--frontend/coverage/src/components/Login.css.html137
-rw-r--r--frontend/coverage/src/components/Login.tsx.html225
-rw-r--r--frontend/coverage/src/components/Settings.css.html453
-rw-r--r--frontend/coverage/src/components/Settings.tsx.html524
-rw-r--r--frontend/coverage/src/components/index.html504
-rw-r--r--frontend/coverage/src/index.html251
-rw-r--r--frontend/src/components/Settings.test.tsx38
-rw-r--r--frontend/src/components/Settings.tsx1
25 files changed, 3907 insertions, 6179 deletions
diff --git a/.thicket/tickets.jsonl b/.thicket/tickets.jsonl
index 5b14ce3..be32f34 100644
--- a/.thicket/tickets.jsonl
+++ b/.thicket/tickets.jsonl
@@ -17,7 +17,7 @@
{"id":"NK-6b4a2e","title":"v2 frontend BLUE LINKS","description":"Make most of the links BLUE and BOLD like in the old legacy version. Thanks","type":"bug","status":"closed","priority":0,"labels":null,"assignee":"","created":"2026-02-14T00:44:47.147880845Z","updated":"2026-02-14T01:09:26.770086073Z"}
{"id":"NK-6o87rr","title":"Vanilla JS: Implement Pagination","description":"Implement 'Load More' or infinite scroll for item list in vanilla JS prototype.","type":"feature","status":"closed","priority":2,"labels":null,"assignee":"","created":"2026-02-14T04:47:40.618957267Z","updated":"2026-02-14T04:47:40.618957267Z"}
{"id":"NK-6q9nyg","title":"Refactor HTTP-dependent functions for testability","description":"Several functions use http.Get or external libraries directly (GetFullContent uses goose, ResolveFeedURL uses http.Get + goquery, imageProxyHandler uses http.Client). Refactor these to accept interfaces for HTTP fetching so they can be unit tested with mocks. This is the primary blocker for reaching 90% coverage.","type":"cleanup","status":"closed","priority":3,"labels":null,"assignee":"","created":"2026-02-13T03:54:37.630148644Z","updated":"2026-02-14T02:44:05.328784994Z"}
-{"id":"NK-7jh6re","title":"sidebar still ugly","description":"still very ugly, even compared to the original v1 static version\n\neither make it nicer or just copy the v1 version more directly","type":"feature","status":"open","priority":0,"labels":null,"assignee":"","created":"2026-02-14T17:59:17.948112909Z","updated":"2026-02-14T17:59:17.948112909Z"}
+{"id":"NK-7jh6re","title":"sidebar still ugly","description":"still very ugly, even compared to the original v1 static version\n\neither make it nicer or just copy the v1 version more directly","type":"feature","status":"closed","priority":0,"labels":null,"assignee":"","created":"2026-02-14T17:59:17.948112909Z","updated":"2026-02-14T18:01:26.48034794Z"}
{"id":"NK-7tzbql","title":"Fix TUI Content View Navigation and Interaction","description":"The TUI content view (reading a single item) is currently non-functional or severely limited. Users cannot easily navigate back, scroll, or interact with the content. This task involves improving the 'viewContent' state in the TUI.","type":"bug","status":"closed","priority":1,"labels":null,"assignee":"","created":"2026-02-13T05:02:57.382793121Z","updated":"2026-02-13T05:06:15.144485446Z"}
{"id":"NK-7xuajb","title":"[security] Add HTTP Security Headers","description":"Add middleware to set standard security headers: Content-Security-Policy (restrict sources), X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy: strict-origin-when-cross-origin.","type":"","status":"closed","priority":2,"labels":null,"assignee":"","created":"2026-02-14T16:35:59.320775688Z","updated":"2026-02-14T17:20:46.397582923Z"}
{"id":"NK-8hu7z1","title":"scrape full text button","description":"add this feature back in to the v2 ui and verify it is still working (not sure if we have any tests)","type":"feature","status":"closed","priority":1,"labels":null,"assignee":"","created":"2026-02-14T17:27:49.815938946Z","updated":"2026-02-14T17:58:19.083695149Z"}
@@ -62,6 +62,7 @@
{"id":"NK-mgmn5m","title":"serve \"legacy\" version UI at /v1/ instead of /","description":"Let's \"softly\" start to deprecated the legacy version by moving it to /v1/ -- ideally this won't require any changes but there may be some relative/absolute URLs to adjust in the static files there or in rouoting","type":"task","status":"closed","priority":0,"labels":null,"assignee":"","created":"2026-02-14T16:41:04.710679944Z","updated":"2026-02-14T17:38:25.35292336Z"}
{"id":"NK-mwf9q2","title":"Implement Tag View","description":"Create frontend view for browsing items by tag/category. Use /tag/:id endpoint.","type":"","status":"closed","priority":2,"labels":null,"assignee":"","created":"2026-02-13T15:04:12.441165286Z","updated":"2026-02-13T18:04:38.644796168Z"}
{"id":"NK-n7nuyy","title":"Fix TypeScript Lint Errors in Tests","description":"There are lint errors in test files regarding jest-dom matchers (toBeInTheDocument, etc). Ensure proper types are included.","type":"bug","status":"closed","priority":3,"labels":null,"assignee":"","created":"2026-02-13T21:50:15.140702806Z","updated":"2026-02-13T21:50:15.140702806Z"}
+{"id":"NK-nx8dhw","title":"fix github ci","description":"seems to be broken, are the right things still in there given all the refactorings","type":"bug","status":"open","priority":1,"labels":null,"assignee":"","created":"2026-02-14T18:02:19.485418738Z","updated":"2026-02-14T18:02:19.485418738Z"}
{"id":"NK-o3n9jf","title":"[security] Run Docker Container as Non-Root User","description":"Update the Dockerfile to create and use a non-privileged user. 1. Create a user (e.g., neko) in the final stage. 2. Ensure the /app/data directory is owned by this user. 3. Switch to this user using USER neko before the CMD.","type":"","status":"closed","priority":2,"labels":null,"assignee":"","created":"2026-02-14T16:35:58.328232962Z","updated":"2026-02-14T17:18:34.748238191Z"}
{"id":"NK-ojdcmq","title":"UI: Add skeleton loaders for feed item loading","description":"The currently 'Loading more...' text is basic. We should add skeleton loaders for a smoother infinite scroll experience.","type":"task","status":"closed","priority":3,"labels":null,"assignee":"","created":"2026-02-13T19:45:07.376295295Z","updated":"2026-02-13T19:45:07.376295295Z"}
{"id":"NK-op5594","title":"Ensure 80% Frontend Test Coverage","description":"Configure coverage reporting in vitest and ensure the frontend codebase maintains at least 80% test coverage.","type":"task","status":"closed","priority":2,"labels":null,"assignee":"","created":"2026-02-13T05:46:24.13314466Z","updated":"2026-02-13T05:50:46.728239299Z"}
diff --git a/frontend/coverage/base.css b/frontend/coverage/base.css
index 8cd9700..f418035 100644
--- a/frontend/coverage/base.css
+++ b/frontend/coverage/base.css
@@ -1,129 +1,71 @@
-body,
-html {
- margin: 0;
- padding: 0;
+body, html {
+ margin:0; padding: 0;
height: 100%;
}
body {
- font-family:
- Helvetica Neue,
- Helvetica,
- Arial;
- font-size: 14px;
- color: #333;
-}
-.small {
- font-size: 12px;
-}
-*,
-*:after,
-*:before {
- -webkit-box-sizing: border-box;
- -moz-box-sizing: border-box;
- box-sizing: border-box;
-}
-h1 {
- font-size: 20px;
- margin: 0;
-}
-h2 {
- font-size: 14px;
-}
+ font-family: Helvetica Neue, Helvetica, Arial;
+ font-size: 14px;
+ color:#333;
+}
+.small { font-size: 12px; }
+*, *:after, *:before {
+ -webkit-box-sizing:border-box;
+ -moz-box-sizing:border-box;
+ box-sizing:border-box;
+ }
+h1 { font-size: 20px; margin: 0;}
+h2 { font-size: 14px; }
pre {
- font:
- 12px/1.4 Consolas,
- 'Liberation Mono',
- Menlo,
- Courier,
- monospace;
- margin: 0;
- padding: 0;
- -moz-tab-size: 2;
- -o-tab-size: 2;
- tab-size: 2;
-}
-a {
- color: #0074d9;
- text-decoration: none;
-}
-a:hover {
- text-decoration: underline;
-}
-.strong {
- font-weight: bold;
-}
-.space-top1 {
- padding: 10px 0 0 0;
-}
-.pad2y {
- padding: 20px 0;
-}
-.pad1y {
- padding: 10px 0;
-}
-.pad2x {
- padding: 0 20px;
-}
-.pad2 {
- padding: 20px;
-}
-.pad1 {
- padding: 10px;
-}
-.space-left2 {
- padding-left: 55px;
-}
-.space-right2 {
- padding-right: 20px;
-}
-.center {
- text-align: center;
-}
-.clearfix {
- display: block;
-}
+ font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
+ margin: 0;
+ padding: 0;
+ -moz-tab-size: 2;
+ -o-tab-size: 2;
+ tab-size: 2;
+}
+a { color:#0074D9; text-decoration:none; }
+a:hover { text-decoration:underline; }
+.strong { font-weight: bold; }
+.space-top1 { padding: 10px 0 0 0; }
+.pad2y { padding: 20px 0; }
+.pad1y { padding: 10px 0; }
+.pad2x { padding: 0 20px; }
+.pad2 { padding: 20px; }
+.pad1 { padding: 10px; }
+.space-left2 { padding-left:55px; }
+.space-right2 { padding-right:20px; }
+.center { text-align:center; }
+.clearfix { display:block; }
.clearfix:after {
- content: '';
- display: block;
- height: 0;
- clear: both;
- visibility: hidden;
-}
-.fl {
- float: left;
-}
-@media only screen and (max-width: 640px) {
- .col3 {
- width: 100%;
- max-width: 100%;
- }
- .hide-mobile {
- display: none !important;
+ content:'';
+ display:block;
+ height:0;
+ clear:both;
+ visibility:hidden;
}
+.fl { float: left; }
+@media only screen and (max-width:640px) {
+ .col3 { width:100%; max-width:100%; }
+ .hide-mobile { display:none!important; }
}
.quiet {
color: #7f7f7f;
- color: rgba(0, 0, 0, 0.5);
-}
-.quiet a {
- opacity: 0.7;
+ color: rgba(0,0,0,0.5);
}
+.quiet a { opacity: 0.7; }
.fraction {
font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
font-size: 10px;
color: #555;
- background: #e8e8e8;
+ background: #E8E8E8;
padding: 4px 5px;
border-radius: 3px;
vertical-align: middle;
}
-div.path a:link,
-div.path a:visited {
- color: #333;
-}
+div.path a:link, div.path a:visited { color: #333; }
table.coverage {
border-collapse: collapse;
margin: 10px 0 0 0;
@@ -136,219 +78,140 @@ table.coverage td {
vertical-align: top;
}
table.coverage td.line-count {
- text-align: right;
- padding: 0 5px 0 20px;
+ text-align: right;
+ padding: 0 5px 0 20px;
}
table.coverage td.line-coverage {
- text-align: right;
- padding-right: 10px;
- min-width: 20px;
+ text-align: right;
+ padding-right: 10px;
+ min-width:20px;
}
table.coverage td span.cline-any {
- display: inline-block;
- padding: 0 5px;
- width: 100%;
+ display: inline-block;
+ padding: 0 5px;
+ width: 100%;
}
.missing-if-branch {
- display: inline-block;
- margin-right: 5px;
- border-radius: 3px;
- position: relative;
- padding: 0 4px;
- background: #333;
- color: yellow;
+ display: inline-block;
+ margin-right: 5px;
+ border-radius: 3px;
+ position: relative;
+ padding: 0 4px;
+ background: #333;
+ color: yellow;
}
.skip-if-branch {
- display: none;
- margin-right: 10px;
- position: relative;
- padding: 0 4px;
- background: #ccc;
- color: white;
+ display: none;
+ margin-right: 10px;
+ position: relative;
+ padding: 0 4px;
+ background: #ccc;
+ color: white;
}
-.missing-if-branch .typ,
-.skip-if-branch .typ {
- color: inherit !important;
+.missing-if-branch .typ, .skip-if-branch .typ {
+ color: inherit !important;
}
.coverage-summary {
border-collapse: collapse;
width: 100%;
}
-.coverage-summary tr {
- border-bottom: 1px solid #bbb;
-}
-.keyline-all {
- border: 1px solid #ddd;
-}
-.coverage-summary td,
-.coverage-summary th {
- padding: 10px;
-}
-.coverage-summary tbody {
- border: 1px solid #bbb;
-}
-.coverage-summary td {
- border-right: 1px solid #bbb;
-}
-.coverage-summary td:last-child {
- border-right: none;
-}
+.coverage-summary tr { border-bottom: 1px solid #bbb; }
+.keyline-all { border: 1px solid #ddd; }
+.coverage-summary td, .coverage-summary th { padding: 10px; }
+.coverage-summary tbody { border: 1px solid #bbb; }
+.coverage-summary td { border-right: 1px solid #bbb; }
+.coverage-summary td:last-child { border-right: none; }
.coverage-summary th {
text-align: left;
font-weight: normal;
white-space: nowrap;
}
-.coverage-summary th.file {
- border-right: none !important;
-}
-.coverage-summary th.pct {
-}
+.coverage-summary th.file { border-right: none !important; }
+.coverage-summary th.pct { }
.coverage-summary th.pic,
.coverage-summary th.abs,
.coverage-summary td.pct,
-.coverage-summary td.abs {
- text-align: right;
-}
-.coverage-summary td.file {
- white-space: nowrap;
-}
-.coverage-summary td.pic {
- min-width: 120px !important;
-}
-.coverage-summary tfoot td {
-}
+.coverage-summary td.abs { text-align: right; }
+.coverage-summary td.file { white-space: nowrap; }
+.coverage-summary td.pic { min-width: 120px !important; }
+.coverage-summary tfoot td { }
.coverage-summary .sorter {
- height: 10px;
- width: 7px;
- display: inline-block;
- margin-left: 0.5em;
- background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
+ height: 10px;
+ width: 7px;
+ display: inline-block;
+ margin-left: 0.5em;
+ background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
}
.coverage-summary .sorted .sorter {
- background-position: 0 -20px;
+ background-position: 0 -20px;
}
.coverage-summary .sorted-desc .sorter {
- background-position: 0 -10px;
-}
-.status-line {
- height: 10px;
+ background-position: 0 -10px;
}
+.status-line { height: 10px; }
/* yellow */
-.cbranch-no {
- background: yellow !important;
- color: #111;
-}
+.cbranch-no { background: yellow !important; color: #111; }
/* dark red */
-.red.solid,
-.status-line.low,
-.low .cover-fill {
- background: #c21f39;
-}
-.low .chart {
- border: 1px solid #c21f39;
-}
+.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
+.low .chart { border:1px solid #C21F39 }
.highlighted,
-.highlighted .cstat-no,
-.highlighted .fstat-no,
-.highlighted .cbranch-no {
- background: #c21f39 !important;
+.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
+ background: #C21F39 !important;
}
/* medium red */
-.cstat-no,
-.fstat-no,
-.cbranch-no,
-.cbranch-no {
- background: #f6c6ce;
-}
+.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
/* light red */
-.low,
-.cline-no {
- background: #fce1e5;
-}
+.low, .cline-no { background:#FCE1E5 }
/* light green */
-.high,
-.cline-yes {
- background: rgb(230, 245, 208);
-}
+.high, .cline-yes { background:rgb(230,245,208) }
/* medium green */
-.cstat-yes {
- background: rgb(161, 215, 106);
-}
+.cstat-yes { background:rgb(161,215,106) }
/* dark green */
-.status-line.high,
-.high .cover-fill {
- background: rgb(77, 146, 33);
-}
-.high .chart {
- border: 1px solid rgb(77, 146, 33);
-}
+.status-line.high, .high .cover-fill { background:rgb(77,146,33) }
+.high .chart { border:1px solid rgb(77,146,33) }
/* dark yellow (gold) */
-.status-line.medium,
-.medium .cover-fill {
- background: #f9cd0b;
-}
-.medium .chart {
- border: 1px solid #f9cd0b;
-}
+.status-line.medium, .medium .cover-fill { background: #f9cd0b; }
+.medium .chart { border:1px solid #f9cd0b; }
/* light yellow */
-.medium {
- background: #fff4c2;
-}
+.medium { background: #fff4c2; }
-.cstat-skip {
- background: #ddd;
- color: #111;
-}
-.fstat-skip {
- background: #ddd;
- color: #111 !important;
-}
-.cbranch-skip {
- background: #ddd !important;
- color: #111;
-}
+.cstat-skip { background: #ddd; color: #111; }
+.fstat-skip { background: #ddd; color: #111 !important; }
+.cbranch-skip { background: #ddd !important; color: #111; }
-span.cline-neutral {
- background: #eaeaea;
-}
+span.cline-neutral { background: #eaeaea; }
.coverage-summary td.empty {
- opacity: 0.5;
- padding-top: 4px;
- padding-bottom: 4px;
- line-height: 1;
- color: #888;
+ opacity: .5;
+ padding-top: 4px;
+ padding-bottom: 4px;
+ line-height: 1;
+ color: #888;
}
-.cover-fill,
-.cover-empty {
- display: inline-block;
+.cover-fill, .cover-empty {
+ display:inline-block;
height: 12px;
}
.chart {
line-height: 0;
}
.cover-empty {
- background: white;
+ background: white;
}
.cover-full {
- border-right: none !important;
+ border-right: none !important;
}
pre.prettyprint {
- border: none !important;
- padding: 0 !important;
- margin: 0 !important;
-}
-.com {
- color: #999 !important;
-}
-.ignore-none {
- color: #999;
- font-weight: normal;
+ border: none !important;
+ padding: 0 !important;
+ margin: 0 !important;
}
+.com { color: #999 !important; }
+.ignore-none { color: #999; font-weight: normal; }
.wrapper {
min-height: 100%;
@@ -356,7 +219,6 @@ pre.prettyprint {
height: 100%;
margin: 0 auto -48px;
}
-.footer,
-.push {
+.footer, .push {
height: 48px;
}
diff --git a/frontend/coverage/block-navigation.js b/frontend/coverage/block-navigation.js
index 05f7569..530d1ed 100644
--- a/frontend/coverage/block-navigation.js
+++ b/frontend/coverage/block-navigation.js
@@ -1,82 +1,87 @@
-
+/* eslint-disable */
var jumpToCode = (function init() {
- // Classes of code we would like to highlight in the file view
- var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
+ // Classes of code we would like to highlight in the file view
+ var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no'];
- // Elements to highlight in the file listing view
- var fileListingElements = ['td.pct.low'];
+ // Elements to highlight in the file listing view
+ var fileListingElements = ['td.pct.low'];
- // We don't want to select elements that are direct descendants of another match
- var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
+ // We don't want to select elements that are direct descendants of another match
+ var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
- // Selector that finds elements on the page to which we can jump
- var selector =
- fileListingElements.join(', ') +
- ', ' +
- notSelector +
- missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
+ // Selector that finds elements on the page to which we can jump
+ var selector =
+ fileListingElements.join(', ') +
+ ', ' +
+ notSelector +
+ missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
- // The NodeList of matching elements
- var missingCoverageElements = document.querySelectorAll(selector);
+ // The NodeList of matching elements
+ var missingCoverageElements = document.querySelectorAll(selector);
- var currentIndex;
+ var currentIndex;
- function toggleClass(index) {
- missingCoverageElements.item(currentIndex).classList.remove('highlighted');
- missingCoverageElements.item(index).classList.add('highlighted');
- }
-
- function makeCurrent(index) {
- toggleClass(index);
- currentIndex = index;
- missingCoverageElements.item(index).scrollIntoView({
- behavior: 'smooth',
- block: 'center',
- inline: 'center',
- });
- }
-
- function goToPrevious() {
- var nextIndex = 0;
- if (typeof currentIndex !== 'number' || currentIndex === 0) {
- nextIndex = missingCoverageElements.length - 1;
- } else if (missingCoverageElements.length > 1) {
- nextIndex = currentIndex - 1;
+ function toggleClass(index) {
+ missingCoverageElements
+ .item(currentIndex)
+ .classList.remove('highlighted');
+ missingCoverageElements.item(index).classList.add('highlighted');
}
- makeCurrent(nextIndex);
- }
+ function makeCurrent(index) {
+ toggleClass(index);
+ currentIndex = index;
+ missingCoverageElements.item(index).scrollIntoView({
+ behavior: 'smooth',
+ block: 'center',
+ inline: 'center'
+ });
+ }
- function goToNext() {
- var nextIndex = 0;
+ function goToPrevious() {
+ var nextIndex = 0;
+ if (typeof currentIndex !== 'number' || currentIndex === 0) {
+ nextIndex = missingCoverageElements.length - 1;
+ } else if (missingCoverageElements.length > 1) {
+ nextIndex = currentIndex - 1;
+ }
- if (typeof currentIndex === 'number' && currentIndex < missingCoverageElements.length - 1) {
- nextIndex = currentIndex + 1;
+ makeCurrent(nextIndex);
}
- makeCurrent(nextIndex);
- }
+ function goToNext() {
+ var nextIndex = 0;
- return function jump(event) {
- if (
- document.getElementById('fileSearch') === document.activeElement &&
- document.activeElement != null
- ) {
- // if we're currently focused on the search input, we don't want to navigate
- return;
- }
+ if (
+ typeof currentIndex === 'number' &&
+ currentIndex < missingCoverageElements.length - 1
+ ) {
+ nextIndex = currentIndex + 1;
+ }
- switch (event.which) {
- case 78: // n
- case 74: // j
- goToNext();
- break;
- case 66: // b
- case 75: // k
- case 80: // p
- goToPrevious();
- break;
+ makeCurrent(nextIndex);
}
- };
+
+ return function jump(event) {
+ if (
+ document.getElementById('fileSearch') === document.activeElement &&
+ document.activeElement != null
+ ) {
+ // if we're currently focused on the search input, we don't want to navigate
+ return;
+ }
+
+ switch (event.which) {
+ case 78: // n
+ case 74: // j
+ goToNext();
+ break;
+ case 66: // b
+ case 75: // k
+ case 80: // p
+ goToPrevious();
+ break;
+ }
+ };
})();
window.addEventListener('keydown', jumpToCode);
diff --git a/frontend/coverage/clover.xml b/frontend/coverage/clover.xml
index f05aeb1..4679d59 100644
--- a/frontend/coverage/clover.xml
+++ b/frontend/coverage/clover.xml
@@ -1,271 +1,341 @@
<?xml version="1.0" encoding="UTF-8"?>
-<coverage generated="1771019398957" clover="3.2.0">
- <project timestamp="1771019398957" name="All files">
- <metrics statements="224" coveredstatements="198" conditionals="142" coveredconditionals="109" methods="73" coveredmethods="63" elements="439" coveredelements="370" complexity="0" loc="224" ncloc="224" packages="2" files="12" classes="12"/>
+<coverage generated="1771092129074" clover="3.2.0">
+ <project timestamp="1771092129074" name="All files">
+ <metrics statements="291" coveredstatements="237" conditionals="197" coveredconditionals="138" methods="93" coveredmethods="70" elements="581" coveredelements="445" complexity="0" loc="291" ncloc="291" packages="2" files="13" classes="13"/>
<package name="src">
- <metrics statements="19" coveredstatements="15" conditionals="6" coveredconditionals="4" methods="9" coveredmethods="7"/>
+ <metrics statements="33" coveredstatements="26" conditionals="26" coveredconditionals="18" methods="10" coveredmethods="7"/>
<file name="App.css" path="/Users/adam/workspace/vibecode/neko/frontend/src/App.css">
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
</file>
<file name="App.tsx" path="/Users/adam/workspace/vibecode/neko/frontend/src/App.tsx">
- <metrics statements="19" coveredstatements="15" conditionals="6" coveredconditionals="4" methods="9" coveredmethods="7"/>
- <line num="8" count="2" type="stmt"/>
+ <metrics statements="22" coveredstatements="16" conditionals="16" coveredconditionals="10" methods="8" coveredmethods="5"/>
<line num="9" count="2" type="stmt"/>
- <line num="11" count="2" type="stmt"/>
- <line num="12" count="1" type="stmt"/>
- <line num="14" count="1" type="cond" truecount="1" falsecount="1"/>
- <line num="15" count="1" type="stmt"/>
- <line num="17" count="0" type="stmt"/>
- <line num="20" count="0" type="stmt"/>
- <line num="23" count="2" type="cond" truecount="2" falsecount="0"/>
- <line num="24" count="1" type="stmt"/>
- <line num="27" count="1" type="cond" truecount="1" falsecount="1"/>
- <line num="28" count="0" type="stmt"/>
- <line num="31" count="1" type="stmt"/>
- <line num="39" count="1" type="stmt"/>
+ <line num="10" count="2" type="stmt"/>
+ <line num="12" count="2" type="stmt"/>
+ <line num="13" count="1" type="stmt"/>
+ <line num="15" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="16" count="1" type="stmt"/>
+ <line num="18" count="0" type="stmt"/>
+ <line num="21" count="0" type="stmt"/>
+ <line num="24" count="2" type="cond" truecount="2" falsecount="0"/>
+ <line num="25" count="1" type="stmt"/>
+ <line num="28" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="29" count="0" type="stmt"/>
+ <line num="32" count="1" type="stmt"/>
<line num="40" count="1" type="stmt"/>
- <line num="45" count="0" type="stmt"/>
- <line num="48" count="1" type="stmt"/>
- <line num="49" count="1" type="stmt"/>
- <line num="73" count="2" type="stmt"/>
+ <line num="42" count="1" type="stmt"/>
+ <line num="50" count="0" type="stmt"/>
+ <line num="73" count="2" type="cond" truecount="2" falsecount="0"/>
+ <line num="75" count="2" type="stmt"/>
+ <line num="76" count="0" type="stmt"/>
+ <line num="77" count="0" type="stmt"/>
+ <line num="80" count="2" type="cond" truecount="1" falsecount="1"/>
+ <line num="82" count="2" type="stmt"/>
+ </file>
+ <file name="utils.ts" path="/Users/adam/workspace/vibecode/neko/frontend/src/utils.ts">
+ <metrics statements="11" coveredstatements="10" conditionals="10" coveredconditionals="8" methods="2" coveredmethods="2"/>
+ <line num="2" count="10" type="stmt"/>
+ <line num="3" count="10" type="stmt"/>
+ <line num="4" count="10" type="cond" truecount="1" falsecount="1"/>
+ <line num="12" count="36" type="cond" truecount="2" falsecount="0"/>
+ <line num="13" count="36" type="stmt"/>
+ <line num="15" count="36" type="cond" truecount="2" falsecount="0"/>
+ <line num="17" count="36" type="cond" truecount="2" falsecount="0"/>
+ <line num="18" count="10" type="stmt"/>
+ <line num="19" count="10" type="cond" truecount="1" falsecount="1"/>
+ <line num="20" count="0" type="stmt"/>
+ <line num="27" count="36" type="stmt"/>
</file>
</package>
<package name="src.components">
- <metrics statements="205" coveredstatements="183" conditionals="136" coveredconditionals="105" methods="64" coveredmethods="56"/>
+ <metrics statements="258" coveredstatements="211" conditionals="171" coveredconditionals="120" methods="83" coveredmethods="63"/>
<file name="FeedItem.css" path="/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItem.css">
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
</file>
<file name="FeedItem.tsx" path="/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItem.tsx">
- <metrics statements="19" coveredstatements="15" conditionals="18" coveredconditionals="16" methods="7" coveredmethods="6"/>
- <line num="10" count="21" type="stmt"/>
- <line num="11" count="21" type="stmt"/>
- <line num="14" count="21" type="stmt"/>
- <line num="15" count="1" type="stmt"/>
- <line num="18" count="21" type="stmt"/>
- <line num="19" count="1" type="stmt"/>
- <line num="21" count="1" type="stmt"/>
- <line num="22" count="1" type="stmt"/>
+ <metrics statements="31" coveredstatements="25" conditionals="23" coveredconditionals="20" methods="12" coveredmethods="10"/>
+ <line num="12" count="33" type="stmt"/>
+ <line num="13" count="33" type="stmt"/>
+ <line num="15" count="33" type="stmt"/>
+ <line num="16" count="16" type="stmt"/>
+ <line num="19" count="33" type="stmt"/>
+ <line num="20" count="1" type="stmt"/>
+ <line num="23" count="33" type="stmt"/>
<line num="24" count="1" type="stmt"/>
- <line num="36" count="1" type="cond" truecount="1" falsecount="1"/>
- <line num="37" count="0" type="stmt"/>
- <line num="39" count="1" type="stmt"/>
+ <line num="26" count="1" type="stmt"/>
+ <line num="27" count="1" type="stmt"/>
+ <line num="29" count="1" type="stmt"/>
+ <line num="41" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="42" count="0" type="stmt"/>
<line num="44" count="1" type="stmt"/>
- <line num="47" count="0" type="stmt"/>
- <line num="49" count="0" type="stmt"/>
- <line num="50" count="0" type="stmt"/>
- <line num="54" count="21" type="stmt"/>
- <line num="59" count="1" type="stmt"/>
+ <line num="49" count="1" type="stmt"/>
+ <line num="52" count="0" type="stmt"/>
+ <line num="54" count="0" type="stmt"/>
+ <line num="55" count="0" type="stmt"/>
+ <line num="59" count="33" type="stmt"/>
<line num="60" count="1" type="stmt"/>
+ <line num="61" count="1" type="stmt"/>
+ <line num="62" count="1" type="stmt"/>
+ <line num="64" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="65" count="1" type="stmt"/>
+ <line num="68" count="1" type="stmt"/>
+ <line num="69" count="1" type="stmt"/>
+ <line num="72" count="0" type="stmt"/>
+ <line num="73" count="0" type="stmt"/>
+ <line num="77" count="33" type="stmt"/>
+ <line num="85" count="1" type="stmt"/>
+ <line num="86" count="1" type="stmt"/>
</file>
<file name="FeedItems.css" path="/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.css">
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
</file>
<file name="FeedItems.tsx" path="/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.tsx">
- <metrics statements="110" coveredstatements="98" conditionals="79" coveredconditionals="61" methods="29" coveredmethods="25"/>
- <line num="8" count="27" type="stmt"/>
+ <metrics statements="115" coveredstatements="102" conditionals="81" coveredconditionals="61" methods="29" coveredmethods="25"/>
<line num="9" count="27" type="stmt"/>
- <line num="10" count="27" type="cond" truecount="2" falsecount="0"/>
- <line num="12" count="27" type="stmt"/>
+ <line num="10" count="27" type="stmt"/>
+ <line num="11" count="27" type="cond" truecount="2" falsecount="0"/>
<line num="13" count="27" type="stmt"/>
<line num="14" count="27" type="stmt"/>
<line num="15" count="27" type="stmt"/>
<line num="16" count="27" type="stmt"/>
+ <line num="17" count="27" type="stmt"/>
<line num="18" count="27" type="stmt"/>
- <line num="19" count="8" type="cond" truecount="2" falsecount="0"/>
- <line num="20" count="1" type="stmt"/>
- <line num="22" count="7" type="stmt"/>
- <line num="23" count="7" type="stmt"/>
- <line num="25" count="8" type="stmt"/>
+ <line num="20" count="27" type="stmt"/>
+ <line num="21" count="8" type="cond" truecount="2" falsecount="0"/>
+ <line num="22" count="1" type="stmt"/>
+ <line num="24" count="7" type="stmt"/>
+ <line num="25" count="7" type="stmt"/>
<line num="27" count="8" type="stmt"/>
- <line num="28" count="8" type="stmt"/>
- <line num="30" count="8" type="cond" truecount="2" falsecount="0"/>
- <line num="31" count="2" type="stmt"/>
- <line num="32" count="6" type="cond" truecount="2" falsecount="0"/>
- <line num="33" count="1" type="stmt"/>
- <line num="36" count="8" type="cond" truecount="2" falsecount="0"/>
- <line num="37" count="1" type="stmt"/>
- <line num="41" count="8" type="cond" truecount="1" falsecount="1"/>
- <line num="42" count="0" type="stmt"/>
- <line num="43" count="8" type="cond" truecount="1" falsecount="1"/>
- <line num="44" count="0" type="stmt"/>
+ <line num="29" count="8" type="stmt"/>
+ <line num="30" count="8" type="stmt"/>
+ <line num="32" count="8" type="cond" truecount="2" falsecount="0"/>
+ <line num="33" count="2" type="stmt"/>
+ <line num="34" count="6" type="cond" truecount="2" falsecount="0"/>
+ <line num="35" count="1" type="stmt"/>
+ <line num="38" count="8" type="cond" truecount="2" falsecount="0"/>
+ <line num="39" count="1" type="stmt"/>
+ <line num="43" count="8" type="stmt"/>
+ <line num="44" count="8" type="cond" truecount="1" falsecount="1"/>
<line num="45" count="0" type="stmt"/>
- <line num="48" count="8" type="stmt"/>
- <line num="51" count="8" type="stmt"/>
- <line num="52" count="8" type="cond" truecount="1" falsecount="1"/>
- <line num="53" count="8" type="stmt"/>
+ <line num="48" count="8" type="cond" truecount="1" falsecount="1"/>
+ <line num="49" count="0" type="stmt"/>
+ <line num="50" count="8" type="cond" truecount="1" falsecount="1"/>
+ <line num="51" count="0" type="stmt"/>
+ <line num="52" count="0" type="stmt"/>
+ <line num="55" count="8" type="cond" truecount="1" falsecount="1"/>
<line num="56" count="8" type="stmt"/>
- <line num="58" count="7" type="cond" truecount="1" falsecount="1"/>
- <line num="59" count="0" type="stmt"/>
- <line num="61" count="7" type="stmt"/>
- <line num="64" count="6" type="cond" truecount="2" falsecount="0"/>
- <line num="65" count="1" type="stmt"/>
- <line num="67" count="5" type="stmt"/>
- <line num="69" count="6" type="stmt"/>
- <line num="70" count="6" type="stmt"/>
- <line num="71" count="6" type="stmt"/>
+ <line num="60" count="8" type="stmt"/>
+ <line num="61" count="8" type="cond" truecount="1" falsecount="1"/>
+ <line num="62" count="8" type="stmt"/>
+ <line num="65" count="8" type="stmt"/>
+ <line num="67" count="7" type="cond" truecount="1" falsecount="1"/>
+ <line num="68" count="0" type="stmt"/>
+ <line num="70" count="7" type="stmt"/>
+ <line num="73" count="6" type="cond" truecount="2" falsecount="0"/>
<line num="74" count="1" type="stmt"/>
- <line num="75" count="1" type="stmt"/>
- <line num="76" count="1" type="stmt"/>
- <line num="80" count="27" type="stmt"/>
- <line num="81" count="7" type="stmt"/>
- <line num="84" count="27" type="stmt"/>
- <line num="86" count="27" type="stmt"/>
- <line num="87" count="23" type="stmt"/>
- <line num="88" count="3" type="cond" truecount="1" falsecount="1"/>
- <line num="90" count="3" type="cond" truecount="2" falsecount="0"/>
- <line num="91" count="2" type="stmt"/>
- <line num="92" count="2" type="stmt"/>
- <line num="93" count="2" type="cond" truecount="1" falsecount="1"/>
- <line num="94" count="2" type="stmt"/>
- <line num="95" count="2" type="cond" truecount="2" falsecount="0"/>
- <line num="96" count="1" type="stmt"/>
+ <line num="76" count="5" type="stmt"/>
+ <line num="78" count="6" type="stmt"/>
+ <line num="79" count="6" type="stmt"/>
+ <line num="80" count="6" type="stmt"/>
+ <line num="83" count="1" type="stmt"/>
+ <line num="84" count="1" type="stmt"/>
+ <line num="85" count="1" type="stmt"/>
+ <line num="89" count="27" type="stmt"/>
+ <line num="90" count="7" type="stmt"/>
+ <line num="91" count="7" type="stmt"/>
+ <line num="95" count="27" type="stmt"/>
+ <line num="96" count="2" type="stmt"/>
+ <line num="97" count="2" type="cond" truecount="1" falsecount="1"/>
<line num="98" count="2" type="stmt"/>
- <line num="100" count="2" type="stmt"/>
- <line num="102" count="1" type="cond" truecount="1" falsecount="1"/>
- <line num="103" count="0" type="stmt"/>
- <line num="104" count="0" type="stmt"/>
- <line num="105" count="0" type="cond" truecount="0" falsecount="2"/>
- <line num="106" count="0" type="stmt"/>
- <line num="108" count="0" type="stmt"/>
- <line num="110" count="1" type="cond" truecount="1" falsecount="1"/>
- <line num="111" count="1" type="stmt"/>
- <line num="112" count="1" type="cond" truecount="3" falsecount="1"/>
- <line num="113" count="1" type="stmt"/>
+ <line num="102" count="27" type="stmt"/>
+ <line num="103" count="2" type="stmt"/>
+ <line num="105" count="3" type="cond" truecount="2" falsecount="0"/>
+ <line num="107" count="2" type="stmt"/>
+ <line num="111" count="0" type="stmt"/>
+ <line num="114" count="27" type="stmt"/>
<line num="115" count="1" type="stmt"/>
- <line num="120" count="23" type="stmt"/>
- <line num="121" count="23" type="stmt"/>
- <line num="124" count="27" type="stmt"/>
- <line num="125" count="2" type="stmt"/>
- <line num="126" count="2" type="cond" truecount="1" falsecount="1"/>
- <line num="127" count="2" type="stmt"/>
- <line num="131" count="27" type="stmt"/>
+ <line num="117" count="2" type="cond" truecount="2" falsecount="0"/>
+ <line num="119" count="1" type="stmt"/>
+ <line num="123" count="0" type="stmt"/>
+ <line num="126" count="27" type="stmt"/>
+ <line num="127" count="23" type="stmt"/>
+ <line num="128" count="3" type="cond" truecount="1" falsecount="1"/>
+ <line num="130" count="3" type="cond" truecount="2" falsecount="0"/>
+ <line num="131" count="2" type="stmt"/>
<line num="132" count="2" type="stmt"/>
- <line num="134" count="3" type="cond" truecount="2" falsecount="0"/>
- <line num="136" count="2" type="stmt"/>
- <line num="140" count="0" type="stmt"/>
- <line num="143" count="27" type="stmt"/>
- <line num="144" count="1" type="stmt"/>
- <line num="146" count="2" type="cond" truecount="2" falsecount="0"/>
- <line num="148" count="1" type="stmt"/>
- <line num="152" count="0" type="stmt"/>
- <line num="155" count="27" type="stmt"/>
- <line num="156" count="24" type="stmt"/>
- <line num="158" count="2" type="stmt"/>
- <line num="160" count="2" type="cond" truecount="2" falsecount="0"/>
- <line num="161" count="1" type="cond" truecount="5" falsecount="1"/>
- <line num="162" count="1" type="stmt"/>
- <line num="164" count="1" type="stmt"/>
- <line num="168" count="1" type="cond" truecount="3" falsecount="1"/>
- <line num="169" count="1" type="stmt"/>
- <line num="170" count="1" type="cond" truecount="4" falsecount="1"/>
- <line num="171" count="1" type="stmt"/>
- <line num="172" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="133" count="2" type="cond" truecount="1" falsecount="1"/>
+ <line num="134" count="2" type="stmt"/>
+ <line num="135" count="2" type="cond" truecount="2" falsecount="0"/>
+ <line num="136" count="1" type="stmt"/>
+ <line num="138" count="2" type="stmt"/>
+ <line num="140" count="2" type="stmt"/>
+ <line num="142" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="143" count="0" type="stmt"/>
+ <line num="144" count="0" type="stmt"/>
+ <line num="145" count="0" type="cond" truecount="0" falsecount="2"/>
+ <line num="146" count="0" type="stmt"/>
+ <line num="148" count="0" type="stmt"/>
+ <line num="150" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="151" count="1" type="stmt"/>
+ <line num="152" count="1" type="cond" truecount="3" falsecount="1"/>
+ <line num="153" count="1" type="stmt"/>
+ <line num="155" count="1" type="stmt"/>
+ <line num="160" count="23" type="stmt"/>
+ <line num="161" count="23" type="stmt"/>
+ <line num="166" count="27" type="stmt"/>
+ <line num="167" count="24" type="stmt"/>
+ <line num="169" count="2" type="stmt"/>
+ <line num="171" count="2" type="cond" truecount="2" falsecount="0"/>
+ <line num="172" count="1" type="cond" truecount="5" falsecount="1"/>
<line num="173" count="1" type="stmt"/>
- <line num="182" count="24" type="stmt"/>
- <line num="183" count="15" type="stmt"/>
- <line num="184" count="15" type="cond" truecount="1" falsecount="1"/>
- <line num="187" count="24" type="stmt"/>
- <line num="188" count="24" type="cond" truecount="2" falsecount="0"/>
- <line num="190" count="24" type="stmt"/>
- <line num="193" count="27" type="cond" truecount="2" falsecount="0"/>
- <line num="194" count="14" type="cond" truecount="2" falsecount="0"/>
- <line num="197" count="13" type="stmt"/>
- <line num="204" count="21" type="stmt"/>
- <line num="209" count="0" type="stmt"/>
+ <line num="175" count="1" type="stmt"/>
+ <line num="179" count="1" type="cond" truecount="3" falsecount="1"/>
+ <line num="180" count="1" type="stmt"/>
+ <line num="181" count="1" type="cond" truecount="4" falsecount="1"/>
+ <line num="182" count="1" type="stmt"/>
+ <line num="183" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="184" count="1" type="stmt"/>
+ <line num="193" count="24" type="stmt"/>
+ <line num="194" count="15" type="stmt"/>
+ <line num="195" count="15" type="cond" truecount="1" falsecount="1"/>
+ <line num="198" count="24" type="stmt"/>
+ <line num="199" count="24" type="cond" truecount="2" falsecount="0"/>
+ <line num="201" count="24" type="stmt"/>
+ <line num="204" count="27" type="cond" truecount="2" falsecount="0"/>
+ <line num="205" count="14" type="cond" truecount="2" falsecount="0"/>
+ <line num="207" count="13" type="stmt"/>
+ <line num="214" count="21" type="stmt"/>
+ <line num="219" count="0" type="stmt"/>
</file>
<file name="FeedList.css" path="/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.css">
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
</file>
<file name="FeedList.tsx" path="/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.tsx">
- <metrics statements="20" coveredstatements="20" conditionals="17" coveredconditionals="14" methods="8" coveredmethods="8"/>
- <line num="7" count="11" type="stmt"/>
- <line num="8" count="11" type="stmt"/>
- <line num="9" count="11" type="stmt"/>
- <line num="10" count="11" type="stmt"/>
- <line num="12" count="11" type="stmt"/>
- <line num="13" count="6" type="stmt"/>
- <line num="15" count="4" type="cond" truecount="1" falsecount="1"/>
- <line num="16" count="4" type="stmt"/>
- <line num="19" count="4" type="cond" truecount="1" falsecount="1"/>
- <line num="20" count="4" type="stmt"/>
- <line num="24" count="4" type="stmt"/>
- <line num="25" count="4" type="stmt"/>
- <line num="26" count="4" type="stmt"/>
- <line num="29" count="1" type="stmt"/>
- <line num="30" count="1" type="stmt"/>
- <line num="34" count="11" type="cond" truecount="2" falsecount="0"/>
- <line num="35" count="5" type="cond" truecount="2" falsecount="0"/>
- <line num="37" count="4" type="stmt"/>
- <line num="53" count="3" type="stmt"/>
- <line num="69" count="3" type="stmt"/>
+ <metrics statements="39" coveredstatements="32" conditionals="37" coveredconditionals="24" methods="17" coveredmethods="11"/>
+ <line num="16" count="13" type="stmt"/>
+ <line num="17" count="13" type="stmt"/>
+ <line num="18" count="13" type="stmt"/>
+ <line num="19" count="13" type="stmt"/>
+ <line num="20" count="13" type="stmt"/>
+ <line num="21" count="13" type="stmt"/>
+ <line num="22" count="13" type="stmt"/>
+ <line num="23" count="13" type="stmt"/>
+ <line num="24" count="13" type="stmt"/>
+ <line num="25" count="13" type="stmt"/>
+ <line num="28" count="13" type="cond" truecount="2" falsecount="0"/>
+ <line num="31" count="13" type="stmt"/>
+ <line num="32" count="0" type="stmt"/>
+ <line num="33" count="0" type="cond" truecount="0" falsecount="2"/>
+ <line num="34" count="0" type="stmt"/>
+ <line num="38" count="13" type="stmt"/>
+ <line num="39" count="2" type="stmt"/>
+ <line num="42" count="13" type="stmt"/>
+ <line num="43" count="6" type="stmt"/>
+ <line num="45" count="4" type="cond" truecount="1" falsecount="1"/>
+ <line num="46" count="4" type="stmt"/>
+ <line num="49" count="4" type="cond" truecount="1" falsecount="1"/>
+ <line num="50" count="4" type="stmt"/>
+ <line num="54" count="4" type="stmt"/>
+ <line num="55" count="4" type="stmt"/>
+ <line num="56" count="4" type="stmt"/>
+ <line num="59" count="1" type="stmt"/>
+ <line num="60" count="1" type="stmt"/>
+ <line num="64" count="13" type="cond" truecount="2" falsecount="0"/>
+ <line num="65" count="7" type="cond" truecount="2" falsecount="0"/>
+ <line num="67" count="6" type="stmt"/>
+ <line num="68" count="1" type="stmt"/>
+ <line num="71" count="6" type="stmt"/>
+ <line num="73" count="0" type="stmt"/>
+ <line num="83" count="0" type="stmt"/>
+ <line num="115" count="4" type="stmt"/>
+ <line num="137" count="2" type="stmt"/>
+ <line num="168" count="0" type="stmt"/>
+ <line num="175" count="0" type="stmt"/>
</file>
<file name="Login.css" path="/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.css">
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
</file>
<file name="Login.tsx" path="/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.tsx">
<metrics statements="17" coveredstatements="17" conditionals="6" coveredconditionals="5" methods="3" coveredmethods="3"/>
- <line num="6" count="14" type="stmt"/>
- <line num="7" count="14" type="stmt"/>
<line num="8" count="14" type="stmt"/>
+ <line num="9" count="14" type="stmt"/>
<line num="10" count="14" type="stmt"/>
- <line num="11" count="3" type="stmt"/>
- <line num="12" count="3" type="stmt"/>
+ <line num="12" count="14" type="stmt"/>
+ <line num="13" count="3" type="stmt"/>
<line num="14" count="3" type="stmt"/>
<line num="16" count="3" type="stmt"/>
- <line num="17" count="3" type="stmt"/>
+ <line num="18" count="3" type="stmt"/>
<line num="19" count="3" type="stmt"/>
- <line num="24" count="2" type="cond" truecount="2" falsecount="0"/>
- <line num="25" count="1" type="stmt"/>
+ <line num="21" count="3" type="stmt"/>
+ <line num="26" count="2" type="cond" truecount="2" falsecount="0"/>
<line num="27" count="1" type="stmt"/>
- <line num="28" count="1" type="cond" truecount="1" falsecount="1"/>
- <line num="31" count="1" type="stmt"/>
- <line num="35" count="14" type="stmt"/>
- <line num="45" count="3" type="stmt"/>
+ <line num="29" count="1" type="stmt"/>
+ <line num="30" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="33" count="1" type="stmt"/>
+ <line num="37" count="14" type="stmt"/>
+ <line num="47" count="3" type="stmt"/>
</file>
<file name="Settings.css" path="/Users/adam/workspace/vibecode/neko/frontend/src/components/Settings.css">
<metrics statements="0" coveredstatements="0" conditionals="0" coveredconditionals="0" methods="0" coveredmethods="0"/>
</file>
<file name="Settings.tsx" path="/Users/adam/workspace/vibecode/neko/frontend/src/components/Settings.tsx">
- <metrics statements="39" coveredstatements="33" conditionals="16" coveredconditionals="9" methods="17" coveredmethods="14"/>
- <line num="6" count="14" type="stmt"/>
+ <metrics statements="56" coveredstatements="35" conditionals="24" coveredconditionals="10" methods="22" coveredmethods="14"/>
<line num="7" count="14" type="stmt"/>
<line num="8" count="14" type="stmt"/>
<line num="9" count="14" type="stmt"/>
- <line num="11" count="14" type="stmt"/>
- <line num="12" count="3" type="stmt"/>
- <line num="15" count="14" type="stmt"/>
+ <line num="10" count="14" type="stmt"/>
+ <line num="12" count="14" type="stmt"/>
+ <line num="14" count="14" type="stmt"/>
+ <line num="15" count="4" type="stmt"/>
<line num="16" count="4" type="stmt"/>
- <line num="17" count="4" type="stmt"/>
- <line num="19" count="4" type="cond" truecount="1" falsecount="1"/>
- <line num="20" count="4" type="stmt"/>
+ <line num="18" count="4" type="cond" truecount="1" falsecount="1"/>
+ <line num="19" count="4" type="stmt"/>
+ <line num="22" count="4" type="stmt"/>
<line num="23" count="4" type="stmt"/>
- <line num="24" count="4" type="stmt"/>
+ <line num="26" count="0" type="stmt"/>
<line num="27" count="0" type="stmt"/>
- <line num="28" count="0" type="stmt"/>
- <line num="32" count="14" type="stmt"/>
- <line num="33" count="1" type="stmt"/>
- <line num="34" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="31" count="14" type="stmt"/>
+ <line num="32" count="3" type="stmt"/>
+ <line num="35" count="14" type="stmt"/>
<line num="36" count="1" type="stmt"/>
- <line num="37" count="1" type="stmt"/>
- <line num="43" count="1" type="cond" truecount="1" falsecount="1"/>
- <line num="44" count="1" type="stmt"/>
+ <line num="37" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="39" count="1" type="stmt"/>
+ <line num="40" count="1" type="stmt"/>
+ <line num="46" count="1" type="cond" truecount="1" falsecount="1"/>
<line num="47" count="1" type="stmt"/>
- <line num="48" count="1" type="stmt"/>
- <line num="51" count="0" type="stmt"/>
- <line num="52" count="0" type="stmt"/>
- <line num="56" count="14" type="stmt"/>
- <line num="57" count="1" type="cond" truecount="1" falsecount="1"/>
- <line num="59" count="1" type="stmt"/>
- <line num="60" count="1" type="stmt"/>
- <line num="64" count="1" type="cond" truecount="1" falsecount="1"/>
- <line num="65" count="1" type="stmt"/>
- <line num="66" count="1" type="stmt"/>
- <line num="69" count="0" type="stmt"/>
- <line num="70" count="0" type="stmt"/>
- <line num="74" count="14" type="stmt"/>
- <line num="84" count="1" type="stmt"/>
- <line num="102" count="5" type="stmt"/>
- <line num="108" count="1" type="stmt"/>
+ <line num="50" count="1" type="stmt"/>
+ <line num="51" count="1" type="stmt"/>
+ <line num="54" count="0" type="stmt"/>
+ <line num="55" count="0" type="stmt"/>
+ <line num="59" count="14" type="stmt"/>
+ <line num="60" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="62" count="1" type="stmt"/>
+ <line num="63" count="1" type="stmt"/>
+ <line num="67" count="1" type="cond" truecount="1" falsecount="1"/>
+ <line num="68" count="1" type="stmt"/>
+ <line num="69" count="1" type="stmt"/>
+ <line num="72" count="0" type="stmt"/>
+ <line num="73" count="0" type="stmt"/>
+ <line num="77" count="14" type="stmt"/>
+ <line num="78" count="0" type="stmt"/>
+ <line num="79" count="0" type="cond" truecount="0" falsecount="2"/>
+ <line num="81" count="0" type="stmt"/>
+ <line num="82" count="0" type="stmt"/>
+ <line num="83" count="0" type="stmt"/>
+ <line num="84" count="0" type="stmt"/>
+ <line num="86" count="0" type="stmt"/>
+ <line num="91" count="0" type="cond" truecount="0" falsecount="2"/>
+ <line num="92" count="0" type="stmt"/>
+ <line num="95" count="0" type="stmt"/>
+ <line num="96" count="0" type="stmt"/>
+ <line num="97" count="0" type="stmt"/>
+ <line num="100" count="0" type="stmt"/>
+ <line num="101" count="0" type="stmt"/>
+ <line num="105" count="14" type="stmt"/>
+ <line num="115" count="1" type="stmt"/>
+ <line num="134" count="0" type="cond" truecount="0" falsecount="2"/>
+ <line num="161" count="5" type="stmt"/>
+ <line num="167" count="1" type="stmt"/>
</file>
</package>
</project>
diff --git a/frontend/coverage/coverage-final.json b/frontend/coverage/coverage-final.json
index e46821b..141aa13 100644
--- a/frontend/coverage/coverage-final.json
+++ b/frontend/coverage/coverage-final.json
@@ -1,2240 +1,14 @@
-{
- "/Users/adam/workspace/vibecode/neko/frontend/src/App.css": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/App.css",
- "statementMap": {},
- "fnMap": {},
- "branchMap": {},
- "s": {},
- "f": {},
- "b": {},
- "meta": { "lastBranch": 0, "lastFunction": 0, "lastStatement": 0, "seen": {} }
- },
- "/Users/adam/workspace/vibecode/neko/frontend/src/App.tsx": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/App.tsx",
- "statementMap": {
- "0": { "start": { "line": 8, "column": 22 }, "end": { "line": 8, "column": null } },
- "1": { "start": { "line": 9, "column": 8 }, "end": { "line": 9, "column": null } },
- "2": { "start": { "line": 11, "column": 2 }, "end": { "line": 21, "column": null } },
- "3": { "start": { "line": 12, "column": 4 }, "end": { "line": 20, "column": null } },
- "4": { "start": { "line": 14, "column": 8 }, "end": { "line": 18, "column": null } },
- "5": { "start": { "line": 15, "column": 10 }, "end": { "line": 15, "column": null } },
- "6": { "start": { "line": 17, "column": 10 }, "end": { "line": 17, "column": null } },
- "7": { "start": { "line": 20, "column": 19 }, "end": { "line": 20, "column": 33 } },
- "8": { "start": { "line": 23, "column": 2 }, "end": { "line": 25, "column": null } },
- "9": { "start": { "line": 24, "column": 4 }, "end": { "line": 24, "column": null } },
- "10": { "start": { "line": 27, "column": 2 }, "end": { "line": 29, "column": null } },
- "11": { "start": { "line": 28, "column": 4 }, "end": { "line": 28, "column": null } },
- "12": { "start": { "line": 31, "column": 2 }, "end": { "line": 31, "column": null } },
- "13": { "start": { "line": 39, "column": 8 }, "end": { "line": 39, "column": null } },
- "14": { "start": { "line": 40, "column": 2 }, "end": { "line": 68, "column": null } },
- "15": { "start": { "line": 45, "column": 33 }, "end": { "line": 45, "column": 56 } },
- "16": { "start": { "line": 48, "column": 12 }, "end": { "line": 49, "column": null } },
- "17": { "start": { "line": 49, "column": 26 }, "end": { "line": 49, "column": 60 } },
- "18": { "start": { "line": 73, "column": 2 }, "end": { "line": 86, "column": null } }
- },
- "fnMap": {
- "0": {
- "name": "RequireAuth",
- "decl": { "start": { "line": 7, "column": 9 }, "end": { "line": 7, "column": 21 } },
- "loc": { "start": { "line": 7, "column": 69 }, "end": { "line": 32, "column": null } },
- "line": 7
- },
- "1": {
- "name": "(anonymous_1)",
- "decl": { "start": { "line": 11, "column": 12 }, "end": { "line": 11, "column": 18 } },
- "loc": { "start": { "line": 11, "column": 18 }, "end": { "line": 21, "column": 5 } },
- "line": 11
- },
- "2": {
- "name": "(anonymous_2)",
- "decl": { "start": { "line": 13, "column": 12 }, "end": { "line": 13, "column": 13 } },
- "loc": { "start": { "line": 13, "column": 21 }, "end": { "line": 19, "column": 7 } },
- "line": 13
- },
- "3": {
- "name": "(anonymous_3)",
- "decl": { "start": { "line": 20, "column": 13 }, "end": { "line": 20, "column": 19 } },
- "loc": { "start": { "line": 20, "column": 19 }, "end": { "line": 20, "column": 33 } },
- "line": 20
- },
- "4": {
- "name": "Dashboard",
- "decl": { "start": { "line": 38, "column": 9 }, "end": { "line": 38, "column": 21 } },
- "loc": { "start": { "line": 38, "column": 21 }, "end": { "line": 70, "column": null } },
- "line": 38
- },
- "5": {
- "name": "(anonymous_5)",
- "decl": { "start": { "line": 45, "column": 27 }, "end": { "line": 45, "column": 33 } },
- "loc": { "start": { "line": 45, "column": 33 }, "end": { "line": 45, "column": 56 } },
- "line": 45
- },
- "6": {
- "name": "(anonymous_6)",
- "decl": { "start": { "line": 47, "column": 27 }, "end": { "line": 47, "column": 33 } },
- "loc": { "start": { "line": 47, "column": 33 }, "end": { "line": 50, "column": 13 } },
- "line": 47
- },
- "7": {
- "name": "(anonymous_7)",
- "decl": { "start": { "line": 49, "column": 20 }, "end": { "line": 49, "column": 26 } },
- "loc": { "start": { "line": 49, "column": 26 }, "end": { "line": 49, "column": 60 } },
- "line": 49
- },
- "8": {
- "name": "App",
- "decl": { "start": { "line": 72, "column": 9 }, "end": { "line": 72, "column": 15 } },
- "loc": { "start": { "line": 72, "column": 15 }, "end": { "line": 88, "column": null } },
- "line": 72
- }
- },
- "branchMap": {
- "0": {
- "loc": { "start": { "line": 14, "column": 8 }, "end": { "line": 18, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 14, "column": 8 }, "end": { "line": 18, "column": null } },
- { "start": { "line": 16, "column": 15 }, "end": { "line": 18, "column": null } }
- ],
- "line": 14
- },
- "1": {
- "loc": { "start": { "line": 23, "column": 2 }, "end": { "line": 25, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 23, "column": 2 }, "end": { "line": 25, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 23
- },
- "2": {
- "loc": { "start": { "line": 27, "column": 2 }, "end": { "line": 29, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 27, "column": 2 }, "end": { "line": 29, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 27
- }
- },
- "s": {
- "0": 2,
- "1": 2,
- "2": 2,
- "3": 1,
- "4": 1,
- "5": 1,
- "6": 0,
- "7": 0,
- "8": 2,
- "9": 1,
- "10": 1,
- "11": 0,
- "12": 1,
- "13": 1,
- "14": 1,
- "15": 0,
- "16": 1,
- "17": 1,
- "18": 2
- },
- "f": { "0": 2, "1": 1, "2": 1, "3": 0, "4": 1, "5": 0, "6": 1, "7": 1, "8": 2 },
- "b": { "0": [1, 0], "1": [1, 1], "2": [0, 1] },
- "meta": {
- "lastBranch": 3,
- "lastFunction": 9,
- "lastStatement": 19,
- "seen": {
- "f:7:9:7:21": 0,
- "s:8:22:8:Infinity": 0,
- "s:9:8:9:Infinity": 1,
- "s:11:2:21:Infinity": 2,
- "f:11:12:11:18": 1,
- "s:12:4:20:Infinity": 3,
- "f:13:12:13:13": 2,
- "b:14:8:18:Infinity:16:15:18:Infinity": 0,
- "s:14:8:18:Infinity": 4,
- "s:15:10:15:Infinity": 5,
- "s:17:10:17:Infinity": 6,
- "f:20:13:20:19": 3,
- "s:20:19:20:33": 7,
- "b:23:2:25:Infinity:undefined:undefined:undefined:undefined": 1,
- "s:23:2:25:Infinity": 8,
- "s:24:4:24:Infinity": 9,
- "b:27:2:29:Infinity:undefined:undefined:undefined:undefined": 2,
- "s:27:2:29:Infinity": 10,
- "s:28:4:28:Infinity": 11,
- "s:31:2:31:Infinity": 12,
- "f:38:9:38:21": 4,
- "s:39:8:39:Infinity": 13,
- "s:40:2:68:Infinity": 14,
- "f:45:27:45:33": 5,
- "s:45:33:45:56": 15,
- "f:47:27:47:33": 6,
- "s:48:12:49:Infinity": 16,
- "f:49:20:49:26": 7,
- "s:49:26:49:60": 17,
- "f:72:9:72:15": 8,
- "s:73:2:86:Infinity": 18
- }
- }
- },
- "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItem.css": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItem.css",
- "statementMap": {},
- "fnMap": {},
- "branchMap": {},
- "s": {},
- "f": {},
- "b": {},
- "meta": { "lastBranch": 0, "lastFunction": 0, "lastStatement": 0, "seen": {} }
- },
- "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItem.tsx": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItem.tsx",
- "statementMap": {
- "0": { "start": { "line": 10, "column": 24 }, "end": { "line": 10, "column": null } },
- "1": { "start": { "line": 11, "column": 30 }, "end": { "line": 11, "column": null } },
- "2": { "start": { "line": 14, "column": 23 }, "end": { "line": 16, "column": null } },
- "3": { "start": { "line": 15, "column": 8 }, "end": { "line": 15, "column": null } },
- "4": { "start": { "line": 18, "column": 23 }, "end": { "line": 52, "column": null } },
- "5": { "start": { "line": 19, "column": 8 }, "end": { "line": 19, "column": null } },
- "6": { "start": { "line": 21, "column": 29 }, "end": { "line": 21, "column": null } },
- "7": { "start": { "line": 22, "column": 8 }, "end": { "line": 22, "column": null } },
- "8": { "start": { "line": 24, "column": 8 }, "end": { "line": 51, "column": null } },
- "9": { "start": { "line": 36, "column": 16 }, "end": { "line": 38, "column": null } },
- "10": { "start": { "line": 37, "column": 20 }, "end": { "line": 37, "column": null } },
- "11": { "start": { "line": 39, "column": 16 }, "end": { "line": 39, "column": null } },
- "12": { "start": { "line": 44, "column": 16 }, "end": { "line": 44, "column": null } },
- "13": { "start": { "line": 47, "column": 16 }, "end": { "line": 47, "column": null } },
- "14": { "start": { "line": 49, "column": 16 }, "end": { "line": 49, "column": null } },
- "15": { "start": { "line": 50, "column": 16 }, "end": { "line": 50, "column": null } },
- "16": { "start": { "line": 54, "column": 4 }, "end": { "line": 82, "column": null } },
- "17": { "start": { "line": 59, "column": 24 }, "end": { "line": 59, "column": null } },
- "18": { "start": { "line": 60, "column": 24 }, "end": { "line": 60, "column": null } }
- },
- "fnMap": {
- "0": {
- "name": "FeedItem",
- "decl": { "start": { "line": 9, "column": 24 }, "end": { "line": 9, "column": 33 } },
- "loc": { "start": { "line": 9, "column": 71 }, "end": { "line": 84, "column": null } },
- "line": 9
- },
- "1": {
- "name": "(anonymous_1)",
- "decl": { "start": { "line": 14, "column": 23 }, "end": { "line": 14, "column": 29 } },
- "loc": { "start": { "line": 14, "column": 29 }, "end": { "line": 16, "column": null } },
- "line": 14
- },
- "2": {
- "name": "(anonymous_2)",
- "decl": { "start": { "line": 18, "column": 23 }, "end": { "line": 18, "column": 24 } },
- "loc": { "start": { "line": 18, "column": 42 }, "end": { "line": 52, "column": null } },
- "line": 18
- },
- "3": {
- "name": "(anonymous_3)",
- "decl": { "start": { "line": 35, "column": 18 }, "end": { "line": 35, "column": 19 } },
- "loc": { "start": { "line": 35, "column": 27 }, "end": { "line": 40, "column": 13 } },
- "line": 35
- },
- "4": {
- "name": "(anonymous_4)",
- "decl": { "start": { "line": 41, "column": 18 }, "end": { "line": 41, "column": 24 } },
- "loc": { "start": { "line": 41, "column": 24 }, "end": { "line": 45, "column": 13 } },
- "line": 41
- },
- "5": {
- "name": "(anonymous_5)",
- "decl": { "start": { "line": 46, "column": 19 }, "end": { "line": 46, "column": 20 } },
- "loc": { "start": { "line": 46, "column": 28 }, "end": { "line": 51, "column": 13 } },
- "line": 46
- },
- "6": {
- "name": "(anonymous_6)",
- "decl": { "start": { "line": 58, "column": 29 }, "end": { "line": 58, "column": 30 } },
- "loc": { "start": { "line": 58, "column": 36 }, "end": { "line": 61, "column": null } },
- "line": 58
- }
- },
- "branchMap": {
- "0": {
- "loc": { "start": { "line": 36, "column": 16 }, "end": { "line": 38, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 36, "column": 16 }, "end": { "line": 38, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 36
- },
- "1": {
- "loc": { "start": { "line": 55, "column": 36 }, "end": { "line": 55, "column": 65 } },
- "type": "cond-expr",
- "locations": [
- { "start": { "line": 55, "column": 48 }, "end": { "line": 55, "column": 57 } },
- { "start": { "line": 55, "column": 57 }, "end": { "line": 55, "column": 65 } }
- ],
- "line": 55
- },
- "2": {
- "loc": { "start": { "line": 55, "column": 69 }, "end": { "line": 55, "column": 93 } },
- "type": "cond-expr",
- "locations": [
- { "start": { "line": 55, "column": 79 }, "end": { "line": 55, "column": 91 } },
- { "start": { "line": 55, "column": 91 }, "end": { "line": 55, "column": 93 } }
- ],
- "line": 55
- },
- "3": {
- "loc": { "start": { "line": 62, "column": 43 }, "end": { "line": 62, "column": 87 } },
- "type": "cond-expr",
- "locations": [
- { "start": { "line": 62, "column": 58 }, "end": { "line": 62, "column": 73 } },
- { "start": { "line": 62, "column": 73 }, "end": { "line": 62, "column": 87 } }
- ],
- "line": 62
- },
- "4": {
- "loc": { "start": { "line": 63, "column": 27 }, "end": { "line": 63, "column": null } },
- "type": "cond-expr",
- "locations": [
- { "start": { "line": 63, "column": 42 }, "end": { "line": 63, "column": 53 } },
- { "start": { "line": 63, "column": 53 }, "end": { "line": 63, "column": null } }
- ],
- "line": 63
- },
- "5": {
- "loc": { "start": { "line": 65, "column": 21 }, "end": { "line": 65, "column": null } },
- "type": "cond-expr",
- "locations": [
- { "start": { "line": 65, "column": 36 }, "end": { "line": 65, "column": 42 } },
- { "start": { "line": 65, "column": 42 }, "end": { "line": 65, "column": null } }
- ],
- "line": 65
- },
- "6": {
- "loc": { "start": { "line": 68, "column": 21 }, "end": { "line": 68, "column": null } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 68, "column": 21 }, "end": { "line": 68, "column": 35 } },
- { "start": { "line": 68, "column": 35 }, "end": { "line": 68, "column": null } }
- ],
- "line": 68
- },
- "7": {
- "loc": { "start": { "line": 74, "column": 21 }, "end": { "line": 74, "column": null } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 74, "column": 21 }, "end": { "line": 74, "column": 40 } },
- { "start": { "line": 74, "column": 40 }, "end": { "line": 74, "column": null } }
- ],
- "line": 74
- },
- "8": {
- "loc": { "start": { "line": 79, "column": 13 }, "end": { "line": 80, "column": null } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 79, "column": 13 }, "end": { "line": 79, "column": null } },
- { "start": { "line": 80, "column": 16 }, "end": { "line": 80, "column": null } }
- ],
- "line": 79
- }
- },
- "s": {
- "0": 21,
- "1": 21,
- "2": 21,
- "3": 1,
- "4": 21,
- "5": 1,
- "6": 1,
- "7": 1,
- "8": 1,
- "9": 1,
- "10": 0,
- "11": 1,
- "12": 1,
- "13": 0,
- "14": 0,
- "15": 0,
- "16": 21,
- "17": 1,
- "18": 1
- },
- "f": { "0": 21, "1": 1, "2": 1, "3": 1, "4": 1, "5": 0, "6": 1 },
- "b": {
- "0": [0, 1],
- "1": [9, 12],
- "2": [1, 20],
- "3": [2, 19],
- "4": [2, 19],
- "5": [2, 19],
- "6": [21, 0],
- "7": [21, 5],
- "8": [21, 4]
- },
- "meta": {
- "lastBranch": 9,
- "lastFunction": 7,
- "lastStatement": 19,
- "seen": {
- "f:9:24:9:33": 0,
- "s:10:24:10:Infinity": 0,
- "s:11:30:11:Infinity": 1,
- "s:14:23:16:Infinity": 2,
- "f:14:23:14:29": 1,
- "s:15:8:15:Infinity": 3,
- "s:18:23:52:Infinity": 4,
- "f:18:23:18:24": 2,
- "s:19:8:19:Infinity": 5,
- "s:21:29:21:Infinity": 6,
- "s:22:8:22:Infinity": 7,
- "s:24:8:51:Infinity": 8,
- "f:35:18:35:19": 3,
- "b:36:16:38:Infinity:undefined:undefined:undefined:undefined": 0,
- "s:36:16:38:Infinity": 9,
- "s:37:20:37:Infinity": 10,
- "s:39:16:39:Infinity": 11,
- "f:41:18:41:24": 4,
- "s:44:16:44:Infinity": 12,
- "f:46:19:46:20": 5,
- "s:47:16:47:Infinity": 13,
- "s:49:16:49:Infinity": 14,
- "s:50:16:50:Infinity": 15,
- "s:54:4:82:Infinity": 16,
- "b:55:48:55:57:55:57:55:65": 1,
- "b:55:79:55:91:55:91:55:93": 2,
- "f:58:29:58:30": 6,
- "s:59:24:59:Infinity": 17,
- "s:60:24:60:Infinity": 18,
- "b:62:58:62:73:62:73:62:87": 3,
- "b:63:42:63:53:63:53:63:Infinity": 4,
- "b:65:36:65:42:65:42:65:Infinity": 5,
- "b:68:21:68:35:68:35:68:Infinity": 6,
- "b:74:21:74:40:74:40:74:Infinity": 7,
- "b:79:13:79:Infinity:80:16:80:Infinity": 8
- }
- }
- },
- "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.css": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.css",
- "statementMap": {},
- "fnMap": {},
- "branchMap": {},
- "s": {},
- "f": {},
- "b": {},
- "meta": { "lastBranch": 0, "lastFunction": 0, "lastStatement": 0, "seen": {} }
- },
- "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.tsx": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.tsx",
- "statementMap": {
- "0": { "start": { "line": 8, "column": 28 }, "end": { "line": 8, "column": null } },
- "1": { "start": { "line": 9, "column": 23 }, "end": { "line": 9, "column": null } },
- "2": { "start": { "line": 10, "column": 21 }, "end": { "line": 10, "column": null } },
- "3": { "start": { "line": 12, "column": 26 }, "end": { "line": 12, "column": null } },
- "4": { "start": { "line": 13, "column": 30 }, "end": { "line": 13, "column": null } },
- "5": { "start": { "line": 14, "column": 38 }, "end": { "line": 14, "column": null } },
- "6": { "start": { "line": 15, "column": 30 }, "end": { "line": 15, "column": null } },
- "7": { "start": { "line": 16, "column": 26 }, "end": { "line": 16, "column": null } },
- "8": { "start": { "line": 18, "column": 23 }, "end": { "line": 78, "column": null } },
- "9": { "start": { "line": 19, "column": 8 }, "end": { "line": 24, "column": null } },
- "10": { "start": { "line": 20, "column": 12 }, "end": { "line": 20, "column": null } },
- "11": { "start": { "line": 22, "column": 12 }, "end": { "line": 22, "column": null } },
- "12": { "start": { "line": 23, "column": 12 }, "end": { "line": 23, "column": null } },
- "13": { "start": { "line": 25, "column": 8 }, "end": { "line": 25, "column": null } },
- "14": { "start": { "line": 27, "column": 18 }, "end": { "line": 27, "column": null } },
- "15": { "start": { "line": 28, "column": 23 }, "end": { "line": 28, "column": null } },
- "16": { "start": { "line": 30, "column": 8 }, "end": { "line": 34, "column": null } },
- "17": { "start": { "line": 31, "column": 12 }, "end": { "line": 31, "column": null } },
- "18": { "start": { "line": 32, "column": 8 }, "end": { "line": 34, "column": null } },
- "19": { "start": { "line": 33, "column": 12 }, "end": { "line": 33, "column": null } },
- "20": { "start": { "line": 36, "column": 8 }, "end": { "line": 38, "column": null } },
- "21": { "start": { "line": 37, "column": 12 }, "end": { "line": 37, "column": null } },
- "22": { "start": { "line": 41, "column": 8 }, "end": { "line": 49, "column": null } },
- "23": { "start": { "line": 42, "column": 12 }, "end": { "line": 42, "column": null } },
- "24": { "start": { "line": 43, "column": 8 }, "end": { "line": 49, "column": null } },
- "25": { "start": { "line": 44, "column": 12 }, "end": { "line": 44, "column": null } },
- "26": { "start": { "line": 45, "column": 12 }, "end": { "line": 45, "column": null } },
- "27": { "start": { "line": 48, "column": 12 }, "end": { "line": 48, "column": null } },
- "28": { "start": { "line": 51, "column": 28 }, "end": { "line": 51, "column": null } },
- "29": { "start": { "line": 52, "column": 8 }, "end": { "line": 54, "column": null } },
- "30": { "start": { "line": 53, "column": 12 }, "end": { "line": 53, "column": null } },
- "31": { "start": { "line": 56, "column": 8 }, "end": { "line": 77, "column": null } },
- "32": { "start": { "line": 58, "column": 16 }, "end": { "line": 60, "column": null } },
- "33": { "start": { "line": 59, "column": 20 }, "end": { "line": 59, "column": null } },
- "34": { "start": { "line": 61, "column": 16 }, "end": { "line": 61, "column": null } },
- "35": { "start": { "line": 64, "column": 16 }, "end": { "line": 68, "column": null } },
- "36": { "start": { "line": 65, "column": 20 }, "end": { "line": 65, "column": null } },
- "37": { "start": { "line": 65, "column": 39 }, "end": { "line": 65, "column": 57 } },
- "38": { "start": { "line": 67, "column": 20 }, "end": { "line": 67, "column": null } },
- "39": { "start": { "line": 69, "column": 16 }, "end": { "line": 69, "column": null } },
- "40": { "start": { "line": 70, "column": 16 }, "end": { "line": 70, "column": null } },
- "41": { "start": { "line": 71, "column": 16 }, "end": { "line": 71, "column": null } },
- "42": { "start": { "line": 74, "column": 16 }, "end": { "line": 74, "column": null } },
- "43": { "start": { "line": 75, "column": 16 }, "end": { "line": 75, "column": null } },
- "44": { "start": { "line": 76, "column": 16 }, "end": { "line": 76, "column": null } },
- "45": { "start": { "line": 80, "column": 4 }, "end": { "line": 82, "column": null } },
- "46": { "start": { "line": 81, "column": 8 }, "end": { "line": 81, "column": null } },
- "47": { "start": { "line": 84, "column": 42 }, "end": { "line": 84, "column": null } },
- "48": { "start": { "line": 86, "column": 4 }, "end": { "line": 122, "column": null } },
- "49": { "start": { "line": 87, "column": 30 }, "end": { "line": 118, "column": null } },
- "50": { "start": { "line": 88, "column": 12 }, "end": { "line": 88, "column": null } },
- "51": { "start": { "line": 88, "column": 36 }, "end": { "line": 88, "column": null } },
- "52": { "start": { "line": 90, "column": 12 }, "end": { "line": 117, "column": null } },
- "53": { "start": { "line": 91, "column": 16 }, "end": { "line": 101, "column": null } },
- "54": { "start": { "line": 92, "column": 38 }, "end": { "line": 92, "column": null } },
- "55": { "start": { "line": 93, "column": 20 }, "end": { "line": 99, "column": null } },
- "56": { "start": { "line": 94, "column": 37 }, "end": { "line": 94, "column": null } },
- "57": { "start": { "line": 95, "column": 24 }, "end": { "line": 97, "column": null } },
- "58": { "start": { "line": 96, "column": 28 }, "end": { "line": 96, "column": null } },
- "59": { "start": { "line": 98, "column": 24 }, "end": { "line": 98, "column": null } },
- "60": { "start": { "line": 100, "column": 20 }, "end": { "line": 100, "column": null } },
- "61": { "start": { "line": 102, "column": 12 }, "end": { "line": 117, "column": null } },
- "62": { "start": { "line": 103, "column": 16 }, "end": { "line": 109, "column": null } },
- "63": { "start": { "line": 104, "column": 38 }, "end": { "line": 104, "column": null } },
- "64": { "start": { "line": 105, "column": 20 }, "end": { "line": 107, "column": null } },
- "65": { "start": { "line": 106, "column": 24 }, "end": { "line": 106, "column": null } },
- "66": { "start": { "line": 108, "column": 20 }, "end": { "line": 108, "column": null } },
- "67": { "start": { "line": 110, "column": 12 }, "end": { "line": 117, "column": null } },
- "68": { "start": { "line": 111, "column": 16 }, "end": { "line": 116, "column": null } },
- "69": { "start": { "line": 112, "column": 20 }, "end": { "line": 114, "column": null } },
- "70": { "start": { "line": 113, "column": 24 }, "end": { "line": 113, "column": null } },
- "71": { "start": { "line": 115, "column": 20 }, "end": { "line": 115, "column": null } },
- "72": { "start": { "line": 120, "column": 8 }, "end": { "line": 120, "column": null } },
- "73": { "start": { "line": 121, "column": 8 }, "end": { "line": 121, "column": null } },
- "74": { "start": { "line": 121, "column": 21 }, "end": { "line": 121, "column": null } },
- "75": { "start": { "line": 124, "column": 25 }, "end": { "line": 129, "column": null } },
- "76": { "start": { "line": 125, "column": 24 }, "end": { "line": 125, "column": null } },
- "77": { "start": { "line": 126, "column": 8 }, "end": { "line": 128, "column": null } },
- "78": { "start": { "line": 127, "column": 12 }, "end": { "line": 127, "column": null } },
- "79": { "start": { "line": 131, "column": 23 }, "end": { "line": 141, "column": null } },
- "80": { "start": { "line": 132, "column": 28 }, "end": { "line": 132, "column": null } },
- "81": { "start": { "line": 134, "column": 8 }, "end": { "line": 134, "column": null } },
- "82": { "start": { "line": 134, "column": 32 }, "end": { "line": 134, "column": 92 } },
- "83": { "start": { "line": 134, "column": 54 }, "end": { "line": 134, "column": 91 } },
- "84": { "start": { "line": 136, "column": 8 }, "end": { "line": 140, "column": null } },
- "85": { "start": { "line": 140, "column": 26 }, "end": { "line": 140, "column": 67 } },
- "86": { "start": { "line": 143, "column": 23 }, "end": { "line": 153, "column": null } },
- "87": { "start": { "line": 144, "column": 28 }, "end": { "line": 144, "column": null } },
- "88": { "start": { "line": 146, "column": 8 }, "end": { "line": 146, "column": null } },
- "89": { "start": { "line": 146, "column": 32 }, "end": { "line": 146, "column": 92 } },
- "90": { "start": { "line": 146, "column": 54 }, "end": { "line": 146, "column": 91 } },
- "91": { "start": { "line": 148, "column": 8 }, "end": { "line": 152, "column": null } },
- "92": { "start": { "line": 152, "column": 26 }, "end": { "line": 152, "column": 69 } },
- "93": { "start": { "line": 155, "column": 4 }, "end": { "line": 191, "column": null } },
- "94": { "start": { "line": 156, "column": 25 }, "end": { "line": 180, "column": null } },
- "95": { "start": { "line": 158, "column": 16 }, "end": { "line": 177, "column": null } },
- "96": { "start": { "line": 160, "column": 20 }, "end": { "line": 165, "column": null } },
- "97": { "start": { "line": 161, "column": 24 }, "end": { "line": 163, "column": null } },
- "98": { "start": { "line": 162, "column": 28 }, "end": { "line": 162, "column": null } },
- "99": { "start": { "line": 164, "column": 24 }, "end": { "line": 164, "column": null } },
- "100": { "start": { "line": 168, "column": 20 }, "end": { "line": 176, "column": null } },
- "101": { "start": { "line": 169, "column": 38 }, "end": { "line": 169, "column": null } },
- "102": { "start": { "line": 170, "column": 24 }, "end": { "line": 175, "column": null } },
- "103": { "start": { "line": 171, "column": 41 }, "end": { "line": 171, "column": null } },
- "104": { "start": { "line": 172, "column": 28 }, "end": { "line": 174, "column": null } },
- "105": { "start": { "line": 173, "column": 32 }, "end": { "line": 173, "column": null } },
- "106": { "start": { "line": 182, "column": 8 }, "end": { "line": 185, "column": null } },
- "107": { "start": { "line": 183, "column": 23 }, "end": { "line": 183, "column": null } },
- "108": { "start": { "line": 184, "column": 12 }, "end": { "line": 184, "column": null } },
- "109": { "start": { "line": 184, "column": 20 }, "end": { "line": 184, "column": null } },
- "110": { "start": { "line": 187, "column": 25 }, "end": { "line": 187, "column": null } },
- "111": { "start": { "line": 188, "column": 8 }, "end": { "line": 188, "column": null } },
- "112": { "start": { "line": 188, "column": 22 }, "end": { "line": 188, "column": null } },
- "113": { "start": { "line": 190, "column": 8 }, "end": { "line": 190, "column": null } },
- "114": { "start": { "line": 190, "column": 21 }, "end": { "line": 190, "column": null } },
- "115": { "start": { "line": 193, "column": 4 }, "end": { "line": 193, "column": null } },
- "116": { "start": { "line": 193, "column": 17 }, "end": { "line": 193, "column": null } },
- "117": { "start": { "line": 194, "column": 4 }, "end": { "line": 194, "column": null } },
- "118": { "start": { "line": 194, "column": 15 }, "end": { "line": 194, "column": null } },
- "119": { "start": { "line": 197, "column": 4 }, "end": { "line": 221, "column": null } },
- "120": { "start": { "line": 204, "column": 24 }, "end": { "line": 212, "column": null } },
- "121": { "start": { "line": 209, "column": 43 }, "end": { "line": 209, "column": null } }
- },
- "fnMap": {
- "0": {
- "name": "FeedItems",
- "decl": { "start": { "line": 7, "column": 24 }, "end": { "line": 7, "column": 36 } },
- "loc": { "start": { "line": 7, "column": 36 }, "end": { "line": 223, "column": null } },
- "line": 7
- },
- "1": {
- "name": "(anonymous_1)",
- "decl": { "start": { "line": 18, "column": 23 }, "end": { "line": 18, "column": 24 } },
- "loc": { "start": { "line": 18, "column": 43 }, "end": { "line": 78, "column": null } },
- "line": 18
- },
- "2": {
- "name": "(anonymous_2)",
- "decl": { "start": { "line": 57, "column": 18 }, "end": { "line": 57, "column": 19 } },
- "loc": { "start": { "line": 57, "column": 27 }, "end": { "line": 62, "column": 13 } },
- "line": 57
- },
- "3": {
- "name": "(anonymous_3)",
- "decl": { "start": { "line": 63, "column": 18 }, "end": { "line": 63, "column": 19 } },
- "loc": { "start": { "line": 63, "column": 28 }, "end": { "line": 72, "column": 13 } },
- "line": 63
- },
- "4": {
- "name": "(anonymous_4)",
- "decl": { "start": { "line": 65, "column": 29 }, "end": { "line": 65, "column": 30 } },
- "loc": { "start": { "line": 65, "column": 39 }, "end": { "line": 65, "column": 57 } },
- "line": 65
- },
- "5": {
- "name": "(anonymous_5)",
- "decl": { "start": { "line": 73, "column": 19 }, "end": { "line": 73, "column": 20 } },
- "loc": { "start": { "line": 73, "column": 28 }, "end": { "line": 77, "column": 13 } },
- "line": 73
- },
- "6": {
- "name": "(anonymous_6)",
- "decl": { "start": { "line": 80, "column": 14 }, "end": { "line": 80, "column": 20 } },
- "loc": { "start": { "line": 80, "column": 20 }, "end": { "line": 82, "column": 7 } },
- "line": 80
- },
- "7": {
- "name": "(anonymous_7)",
- "decl": { "start": { "line": 86, "column": 14 }, "end": { "line": 86, "column": 20 } },
- "loc": { "start": { "line": 86, "column": 20 }, "end": { "line": 122, "column": 7 } },
- "line": 86
- },
- "8": {
- "name": "(anonymous_8)",
- "decl": { "start": { "line": 87, "column": 30 }, "end": { "line": 87, "column": 31 } },
- "loc": { "start": { "line": 87, "column": 52 }, "end": { "line": 118, "column": null } },
- "line": 87
- },
- "9": {
- "name": "(anonymous_9)",
- "decl": { "start": { "line": 91, "column": 33 }, "end": { "line": 91, "column": 34 } },
- "loc": { "start": { "line": 91, "column": 43 }, "end": { "line": 101, "column": 17 } },
- "line": 91
- },
- "10": {
- "name": "(anonymous_10)",
- "decl": { "start": { "line": 103, "column": 33 }, "end": { "line": 103, "column": 34 } },
- "loc": { "start": { "line": 103, "column": 43 }, "end": { "line": 109, "column": 17 } },
- "line": 103
- },
- "11": {
- "name": "(anonymous_11)",
- "decl": { "start": { "line": 111, "column": 33 }, "end": { "line": 111, "column": 34 } },
- "loc": { "start": { "line": 111, "column": 51 }, "end": { "line": 116, "column": 17 } },
- "line": 111
- },
- "12": {
- "name": "(anonymous_12)",
- "decl": { "start": { "line": 121, "column": 15 }, "end": { "line": 121, "column": 21 } },
- "loc": { "start": { "line": 121, "column": 21 }, "end": { "line": 121, "column": null } },
- "line": 121
- },
- "13": {
- "name": "(anonymous_13)",
- "decl": { "start": { "line": 124, "column": 25 }, "end": { "line": 124, "column": 26 } },
- "loc": { "start": { "line": 124, "column": 44 }, "end": { "line": 129, "column": null } },
- "line": 124
- },
- "14": {
- "name": "(anonymous_14)",
- "decl": { "start": { "line": 131, "column": 23 }, "end": { "line": 131, "column": 24 } },
- "loc": { "start": { "line": 131, "column": 39 }, "end": { "line": 141, "column": null } },
- "line": 131
- },
- "15": {
- "name": "(anonymous_15)",
- "decl": { "start": { "line": 134, "column": 17 }, "end": { "line": 134, "column": 18 } },
- "loc": { "start": { "line": 134, "column": 32 }, "end": { "line": 134, "column": 92 } },
- "line": 134
- },
- "16": {
- "name": "(anonymous_16)",
- "decl": { "start": { "line": 134, "column": 46 }, "end": { "line": 134, "column": 47 } },
- "loc": { "start": { "line": 134, "column": 54 }, "end": { "line": 134, "column": 91 } },
- "line": 134
- },
- "17": {
- "name": "(anonymous_17)",
- "decl": { "start": { "line": 140, "column": 17 }, "end": { "line": 140, "column": 18 } },
- "loc": { "start": { "line": 140, "column": 26 }, "end": { "line": 140, "column": 67 } },
- "line": 140
- },
- "18": {
- "name": "(anonymous_18)",
- "decl": { "start": { "line": 143, "column": 23 }, "end": { "line": 143, "column": 24 } },
- "loc": { "start": { "line": 143, "column": 39 }, "end": { "line": 153, "column": null } },
- "line": 143
- },
- "19": {
- "name": "(anonymous_19)",
- "decl": { "start": { "line": 146, "column": 17 }, "end": { "line": 146, "column": 18 } },
- "loc": { "start": { "line": 146, "column": 32 }, "end": { "line": 146, "column": 92 } },
- "line": 146
- },
- "20": {
- "name": "(anonymous_20)",
- "decl": { "start": { "line": 146, "column": 46 }, "end": { "line": 146, "column": 47 } },
- "loc": { "start": { "line": 146, "column": 54 }, "end": { "line": 146, "column": 91 } },
- "line": 146
- },
- "21": {
- "name": "(anonymous_21)",
- "decl": { "start": { "line": 152, "column": 17 }, "end": { "line": 152, "column": 18 } },
- "loc": { "start": { "line": 152, "column": 26 }, "end": { "line": 152, "column": 69 } },
- "line": 152
- },
- "22": {
- "name": "(anonymous_22)",
- "decl": { "start": { "line": 155, "column": 14 }, "end": { "line": 155, "column": 20 } },
- "loc": { "start": { "line": 155, "column": 20 }, "end": { "line": 191, "column": 7 } },
- "line": 155
- },
- "23": {
- "name": "(anonymous_23)",
- "decl": { "start": { "line": 157, "column": 12 }, "end": { "line": 157, "column": 13 } },
- "loc": { "start": { "line": 157, "column": 25 }, "end": { "line": 178, "column": null } },
- "line": 157
- },
- "24": {
- "name": "(anonymous_24)",
- "decl": { "start": { "line": 158, "column": 32 }, "end": { "line": 158, "column": 33 } },
- "loc": { "start": { "line": 158, "column": 43 }, "end": { "line": 177, "column": 17 } },
- "line": 158
- },
- "25": {
- "name": "(anonymous_25)",
- "decl": { "start": { "line": 182, "column": 22 }, "end": { "line": 182, "column": 23 } },
- "loc": { "start": { "line": 182, "column": 36 }, "end": { "line": 185, "column": 9 } },
- "line": 182
- },
- "26": {
- "name": "(anonymous_26)",
- "decl": { "start": { "line": 190, "column": 15 }, "end": { "line": 190, "column": 21 } },
- "loc": { "start": { "line": 190, "column": 21 }, "end": { "line": 190, "column": null } },
- "line": 190
- },
- "27": {
- "name": "(anonymous_27)",
- "decl": { "start": { "line": 203, "column": 31 }, "end": { "line": 203, "column": 32 } },
- "loc": { "start": { "line": 204, "column": 24 }, "end": { "line": 212, "column": null } },
- "line": 204
- },
- "28": {
- "name": "(anonymous_28)",
- "decl": { "start": { "line": 209, "column": 37 }, "end": { "line": 209, "column": 43 } },
- "loc": { "start": { "line": 209, "column": 43 }, "end": { "line": 209, "column": null } },
- "line": 209
- }
- },
- "branchMap": {
- "0": {
- "loc": { "start": { "line": 10, "column": 21 }, "end": { "line": 10, "column": null } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 10, "column": 21 }, "end": { "line": 10, "column": 51 } },
- { "start": { "line": 10, "column": 51 }, "end": { "line": 10, "column": null } }
- ],
- "line": 10
- },
- "1": {
- "loc": { "start": { "line": 19, "column": 8 }, "end": { "line": 24, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 19, "column": 8 }, "end": { "line": 24, "column": null } },
- { "start": { "line": 21, "column": 15 }, "end": { "line": 24, "column": null } }
- ],
- "line": 19
- },
- "2": {
- "loc": { "start": { "line": 30, "column": 8 }, "end": { "line": 34, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 30, "column": 8 }, "end": { "line": 34, "column": null } },
- { "start": { "line": 32, "column": 8 }, "end": { "line": 34, "column": null } }
- ],
- "line": 30
- },
- "3": {
- "loc": { "start": { "line": 32, "column": 8 }, "end": { "line": 34, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 32, "column": 8 }, "end": { "line": 34, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 32
- },
- "4": {
- "loc": { "start": { "line": 36, "column": 8 }, "end": { "line": 38, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 36, "column": 8 }, "end": { "line": 38, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 36
- },
- "5": {
- "loc": { "start": { "line": 41, "column": 8 }, "end": { "line": 49, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 41, "column": 8 }, "end": { "line": 49, "column": null } },
- { "start": { "line": 43, "column": 8 }, "end": { "line": 49, "column": null } }
- ],
- "line": 41
- },
- "6": {
- "loc": { "start": { "line": 43, "column": 8 }, "end": { "line": 49, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 43, "column": 8 }, "end": { "line": 49, "column": null } },
- { "start": { "line": 46, "column": 15 }, "end": { "line": 49, "column": null } }
- ],
- "line": 43
- },
- "7": {
- "loc": { "start": { "line": 52, "column": 8 }, "end": { "line": 54, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 52, "column": 8 }, "end": { "line": 54, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 52
- },
- "8": {
- "loc": { "start": { "line": 58, "column": 16 }, "end": { "line": 60, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 58, "column": 16 }, "end": { "line": 60, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 58
- },
- "9": {
- "loc": { "start": { "line": 64, "column": 16 }, "end": { "line": 68, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 64, "column": 16 }, "end": { "line": 68, "column": null } },
- { "start": { "line": 66, "column": 23 }, "end": { "line": 68, "column": null } }
- ],
- "line": 64
- },
- "10": {
- "loc": { "start": { "line": 88, "column": 12 }, "end": { "line": 88, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 88, "column": 12 }, "end": { "line": 88, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 88
- },
- "11": {
- "loc": { "start": { "line": 90, "column": 12 }, "end": { "line": 117, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 90, "column": 12 }, "end": { "line": 117, "column": null } },
- { "start": { "line": 102, "column": 12 }, "end": { "line": 117, "column": null } }
- ],
- "line": 90
- },
- "12": {
- "loc": { "start": { "line": 93, "column": 20 }, "end": { "line": 99, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 93, "column": 20 }, "end": { "line": 99, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 93
- },
- "13": {
- "loc": { "start": { "line": 95, "column": 24 }, "end": { "line": 97, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 95, "column": 24 }, "end": { "line": 97, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 95
- },
- "14": {
- "loc": { "start": { "line": 102, "column": 12 }, "end": { "line": 117, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 102, "column": 12 }, "end": { "line": 117, "column": null } },
- { "start": { "line": 110, "column": 12 }, "end": { "line": 117, "column": null } }
- ],
- "line": 102
- },
- "15": {
- "loc": { "start": { "line": 105, "column": 20 }, "end": { "line": 107, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 105, "column": 20 }, "end": { "line": 107, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 105
- },
- "16": {
- "loc": { "start": { "line": 110, "column": 12 }, "end": { "line": 117, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 110, "column": 12 }, "end": { "line": 117, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 110
- },
- "17": {
- "loc": { "start": { "line": 112, "column": 20 }, "end": { "line": 114, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 112, "column": 20 }, "end": { "line": 114, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 112
- },
- "18": {
- "loc": { "start": { "line": 112, "column": 24 }, "end": { "line": 112, "column": 74 } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 112, "column": 24 }, "end": { "line": 112, "column": 45 } },
- { "start": { "line": 112, "column": 45 }, "end": { "line": 112, "column": 74 } }
- ],
- "line": 112
- },
- "19": {
- "loc": { "start": { "line": 126, "column": 8 }, "end": { "line": 128, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 126, "column": 8 }, "end": { "line": 128, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 126
- },
- "20": {
- "loc": { "start": { "line": 134, "column": 54 }, "end": { "line": 134, "column": 91 } },
- "type": "cond-expr",
- "locations": [
- { "start": { "line": 134, "column": 75 }, "end": { "line": 134, "column": 89 } },
- { "start": { "line": 134, "column": 89 }, "end": { "line": 134, "column": 91 } }
- ],
- "line": 134
- },
- "21": {
- "loc": { "start": { "line": 146, "column": 54 }, "end": { "line": 146, "column": 91 } },
- "type": "cond-expr",
- "locations": [
- { "start": { "line": 146, "column": 75 }, "end": { "line": 146, "column": 89 } },
- { "start": { "line": 146, "column": 89 }, "end": { "line": 146, "column": 91 } }
- ],
- "line": 146
- },
- "22": {
- "loc": { "start": { "line": 160, "column": 20 }, "end": { "line": 165, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 160, "column": 20 }, "end": { "line": 165, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 160
- },
- "23": {
- "loc": { "start": { "line": 161, "column": 24 }, "end": { "line": 163, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 161, "column": 24 }, "end": { "line": 163, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 161
- },
- "24": {
- "loc": { "start": { "line": 161, "column": 28 }, "end": { "line": 161, "column": 97 } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 161, "column": 28 }, "end": { "line": 161, "column": 52 } },
- { "start": { "line": 161, "column": 52 }, "end": { "line": 161, "column": 68 } },
- { "start": { "line": 161, "column": 68 }, "end": { "line": 161, "column": 79 } },
- { "start": { "line": 161, "column": 79 }, "end": { "line": 161, "column": 97 } }
- ],
- "line": 161
- },
- "25": {
- "loc": { "start": { "line": 168, "column": 20 }, "end": { "line": 176, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 168, "column": 20 }, "end": { "line": 176, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 168
- },
- "26": {
- "loc": { "start": { "line": 168, "column": 24 }, "end": { "line": 168, "column": 83 } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 168, "column": 24 }, "end": { "line": 168, "column": 49 } },
- { "start": { "line": 168, "column": 49 }, "end": { "line": 168, "column": 83 } }
- ],
- "line": 168
- },
- "27": {
- "loc": { "start": { "line": 170, "column": 24 }, "end": { "line": 175, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 170, "column": 24 }, "end": { "line": 175, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 170
- },
- "28": {
- "loc": { "start": { "line": 170, "column": 28 }, "end": { "line": 170, "column": 81 } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 170, "column": 28 }, "end": { "line": 170, "column": 45 } },
- { "start": { "line": 170, "column": 45 }, "end": { "line": 170, "column": 59 } },
- { "start": { "line": 170, "column": 59 }, "end": { "line": 170, "column": 81 } }
- ],
- "line": 170
- },
- "29": {
- "loc": { "start": { "line": 172, "column": 28 }, "end": { "line": 174, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 172, "column": 28 }, "end": { "line": 174, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 172
- },
- "30": {
- "loc": { "start": { "line": 184, "column": 12 }, "end": { "line": 184, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 184, "column": 12 }, "end": { "line": 184, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 184
- },
- "31": {
- "loc": { "start": { "line": 188, "column": 8 }, "end": { "line": 188, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 188, "column": 8 }, "end": { "line": 188, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 188
- },
- "32": {
- "loc": { "start": { "line": 193, "column": 4 }, "end": { "line": 193, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 193, "column": 4 }, "end": { "line": 193, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 193
- },
- "33": {
- "loc": { "start": { "line": 194, "column": 4 }, "end": { "line": 194, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 194, "column": 4 }, "end": { "line": 194, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 194
- },
- "34": {
- "loc": { "start": { "line": 199, "column": 13 }, "end": { "line": 219, "column": null } },
- "type": "cond-expr",
- "locations": [
- { "start": { "line": 200, "column": 16 }, "end": { "line": 200, "column": null } },
- { "start": { "line": 202, "column": 16 }, "end": { "line": 219, "column": null } }
- ],
- "line": 199
- },
- "35": {
- "loc": { "start": { "line": 208, "column": 39 }, "end": { "line": 208, "column": null } },
- "type": "cond-expr",
- "locations": [
- { "start": { "line": 208, "column": 65 }, "end": { "line": 208, "column": 93 } },
- { "start": { "line": 208, "column": 93 }, "end": { "line": 208, "column": null } }
- ],
- "line": 208
- },
- "36": {
- "loc": { "start": { "line": 214, "column": 21 }, "end": { "line": 217, "column": null } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 214, "column": 21 }, "end": { "line": 214, "column": null } },
- { "start": { "line": 215, "column": 24 }, "end": { "line": 217, "column": null } }
- ],
- "line": 214
- },
- "37": {
- "loc": { "start": { "line": 216, "column": 29 }, "end": { "line": 216, "column": null } },
- "type": "cond-expr",
- "locations": [
- { "start": { "line": 216, "column": 43 }, "end": { "line": 216, "column": 63 } },
- { "start": { "line": 216, "column": 63 }, "end": { "line": 216, "column": null } }
- ],
- "line": 216
- }
- },
- "s": {
- "0": 27,
- "1": 27,
- "2": 27,
- "3": 27,
- "4": 27,
- "5": 27,
- "6": 27,
- "7": 27,
- "8": 27,
- "9": 8,
- "10": 1,
- "11": 7,
- "12": 7,
- "13": 8,
- "14": 8,
- "15": 8,
- "16": 8,
- "17": 2,
- "18": 6,
- "19": 1,
- "20": 8,
- "21": 1,
- "22": 8,
- "23": 0,
- "24": 8,
- "25": 0,
- "26": 0,
- "27": 8,
- "28": 8,
- "29": 8,
- "30": 8,
- "31": 8,
- "32": 7,
- "33": 0,
- "34": 7,
- "35": 6,
- "36": 1,
- "37": 1,
- "38": 5,
- "39": 6,
- "40": 6,
- "41": 6,
- "42": 1,
- "43": 1,
- "44": 1,
- "45": 27,
- "46": 7,
- "47": 27,
- "48": 27,
- "49": 23,
- "50": 3,
- "51": 0,
- "52": 3,
- "53": 2,
- "54": 2,
- "55": 2,
- "56": 2,
- "57": 2,
- "58": 1,
- "59": 2,
- "60": 2,
- "61": 1,
- "62": 0,
- "63": 0,
- "64": 0,
- "65": 0,
- "66": 0,
- "67": 1,
- "68": 1,
- "69": 1,
- "70": 1,
- "71": 1,
- "72": 23,
- "73": 23,
- "74": 23,
- "75": 27,
- "76": 2,
- "77": 2,
- "78": 2,
- "79": 27,
- "80": 2,
- "81": 2,
- "82": 2,
- "83": 3,
- "84": 2,
- "85": 0,
- "86": 27,
- "87": 1,
- "88": 1,
- "89": 1,
- "90": 2,
- "91": 1,
- "92": 0,
- "93": 27,
- "94": 24,
- "95": 2,
- "96": 2,
- "97": 1,
- "98": 1,
- "99": 1,
- "100": 1,
- "101": 1,
- "102": 1,
- "103": 1,
- "104": 1,
- "105": 1,
- "106": 24,
- "107": 15,
- "108": 15,
- "109": 15,
- "110": 24,
- "111": 24,
- "112": 10,
- "113": 24,
- "114": 24,
- "115": 27,
- "116": 13,
- "117": 14,
- "118": 14,
- "119": 13,
- "120": 21,
- "121": 0
- },
- "f": {
- "0": 27,
- "1": 8,
- "2": 7,
- "3": 6,
- "4": 1,
- "5": 1,
- "6": 7,
- "7": 23,
- "8": 3,
- "9": 2,
- "10": 0,
- "11": 1,
- "12": 23,
- "13": 2,
- "14": 2,
- "15": 2,
- "16": 3,
- "17": 0,
- "18": 1,
- "19": 1,
- "20": 2,
- "21": 0,
- "22": 24,
- "23": 2,
- "24": 2,
- "25": 15,
- "26": 24,
- "27": 21,
- "28": 0
- },
- "b": {
- "0": [27, 27],
- "1": [1, 7],
- "2": [2, 6],
- "3": [1, 5],
- "4": [1, 7],
- "5": [0, 8],
- "6": [0, 8],
- "7": [8, 0],
- "8": [0, 7],
- "9": [1, 5],
- "10": [0, 3],
- "11": [2, 1],
- "12": [2, 0],
- "13": [1, 1],
- "14": [0, 1],
- "15": [0, 0],
- "16": [1, 0],
- "17": [1, 0],
- "18": [1, 1],
- "19": [2, 0],
- "20": [2, 1],
- "21": [1, 1],
- "22": [1, 1],
- "23": [1, 0],
- "24": [1, 1, 1, 1],
- "25": [1, 0],
- "26": [1, 1],
- "27": [1, 0],
- "28": [1, 1, 1],
- "29": [1, 0],
- "30": [15, 0],
- "31": [10, 14],
- "32": [13, 14],
- "33": [1, 13],
- "34": [0, 13],
- "35": [5, 16],
- "36": [13, 13],
- "37": [1, 12]
- },
- "meta": {
- "lastBranch": 38,
- "lastFunction": 29,
- "lastStatement": 122,
- "seen": {
- "f:7:24:7:36": 0,
- "s:8:28:8:Infinity": 0,
- "s:9:23:9:Infinity": 1,
- "s:10:21:10:Infinity": 2,
- "b:10:21:10:51:10:51:10:Infinity": 0,
- "s:12:26:12:Infinity": 3,
- "s:13:30:13:Infinity": 4,
- "s:14:38:14:Infinity": 5,
- "s:15:30:15:Infinity": 6,
- "s:16:26:16:Infinity": 7,
- "s:18:23:78:Infinity": 8,
- "f:18:23:18:24": 1,
- "b:19:8:24:Infinity:21:15:24:Infinity": 1,
- "s:19:8:24:Infinity": 9,
- "s:20:12:20:Infinity": 10,
- "s:22:12:22:Infinity": 11,
- "s:23:12:23:Infinity": 12,
- "s:25:8:25:Infinity": 13,
- "s:27:18:27:Infinity": 14,
- "s:28:23:28:Infinity": 15,
- "b:30:8:34:Infinity:32:8:34:Infinity": 2,
- "s:30:8:34:Infinity": 16,
- "s:31:12:31:Infinity": 17,
- "b:32:8:34:Infinity:undefined:undefined:undefined:undefined": 3,
- "s:32:8:34:Infinity": 18,
- "s:33:12:33:Infinity": 19,
- "b:36:8:38:Infinity:undefined:undefined:undefined:undefined": 4,
- "s:36:8:38:Infinity": 20,
- "s:37:12:37:Infinity": 21,
- "b:41:8:49:Infinity:43:8:49:Infinity": 5,
- "s:41:8:49:Infinity": 22,
- "s:42:12:42:Infinity": 23,
- "b:43:8:49:Infinity:46:15:49:Infinity": 6,
- "s:43:8:49:Infinity": 24,
- "s:44:12:44:Infinity": 25,
- "s:45:12:45:Infinity": 26,
- "s:48:12:48:Infinity": 27,
- "s:51:28:51:Infinity": 28,
- "b:52:8:54:Infinity:undefined:undefined:undefined:undefined": 7,
- "s:52:8:54:Infinity": 29,
- "s:53:12:53:Infinity": 30,
- "s:56:8:77:Infinity": 31,
- "f:57:18:57:19": 2,
- "b:58:16:60:Infinity:undefined:undefined:undefined:undefined": 8,
- "s:58:16:60:Infinity": 32,
- "s:59:20:59:Infinity": 33,
- "s:61:16:61:Infinity": 34,
- "f:63:18:63:19": 3,
- "b:64:16:68:Infinity:66:23:68:Infinity": 9,
- "s:64:16:68:Infinity": 35,
- "s:65:20:65:Infinity": 36,
- "f:65:29:65:30": 4,
- "s:65:39:65:57": 37,
- "s:67:20:67:Infinity": 38,
- "s:69:16:69:Infinity": 39,
- "s:70:16:70:Infinity": 40,
- "s:71:16:71:Infinity": 41,
- "f:73:19:73:20": 5,
- "s:74:16:74:Infinity": 42,
- "s:75:16:75:Infinity": 43,
- "s:76:16:76:Infinity": 44,
- "s:80:4:82:Infinity": 45,
- "f:80:14:80:20": 6,
- "s:81:8:81:Infinity": 46,
- "s:84:42:84:Infinity": 47,
- "s:86:4:122:Infinity": 48,
- "f:86:14:86:20": 7,
- "s:87:30:118:Infinity": 49,
- "f:87:30:87:31": 8,
- "b:88:12:88:Infinity:undefined:undefined:undefined:undefined": 10,
- "s:88:12:88:Infinity": 50,
- "s:88:36:88:Infinity": 51,
- "b:90:12:117:Infinity:102:12:117:Infinity": 11,
- "s:90:12:117:Infinity": 52,
- "s:91:16:101:Infinity": 53,
- "f:91:33:91:34": 9,
- "s:92:38:92:Infinity": 54,
- "b:93:20:99:Infinity:undefined:undefined:undefined:undefined": 12,
- "s:93:20:99:Infinity": 55,
- "s:94:37:94:Infinity": 56,
- "b:95:24:97:Infinity:undefined:undefined:undefined:undefined": 13,
- "s:95:24:97:Infinity": 57,
- "s:96:28:96:Infinity": 58,
- "s:98:24:98:Infinity": 59,
- "s:100:20:100:Infinity": 60,
- "b:102:12:117:Infinity:110:12:117:Infinity": 14,
- "s:102:12:117:Infinity": 61,
- "s:103:16:109:Infinity": 62,
- "f:103:33:103:34": 10,
- "s:104:38:104:Infinity": 63,
- "b:105:20:107:Infinity:undefined:undefined:undefined:undefined": 15,
- "s:105:20:107:Infinity": 64,
- "s:106:24:106:Infinity": 65,
- "s:108:20:108:Infinity": 66,
- "b:110:12:117:Infinity:undefined:undefined:undefined:undefined": 16,
- "s:110:12:117:Infinity": 67,
- "s:111:16:116:Infinity": 68,
- "f:111:33:111:34": 11,
- "b:112:20:114:Infinity:undefined:undefined:undefined:undefined": 17,
- "s:112:20:114:Infinity": 69,
- "b:112:24:112:45:112:45:112:74": 18,
- "s:113:24:113:Infinity": 70,
- "s:115:20:115:Infinity": 71,
- "s:120:8:120:Infinity": 72,
- "s:121:8:121:Infinity": 73,
- "f:121:15:121:21": 12,
- "s:121:21:121:Infinity": 74,
- "s:124:25:129:Infinity": 75,
- "f:124:25:124:26": 13,
- "s:125:24:125:Infinity": 76,
- "b:126:8:128:Infinity:undefined:undefined:undefined:undefined": 19,
- "s:126:8:128:Infinity": 77,
- "s:127:12:127:Infinity": 78,
- "s:131:23:141:Infinity": 79,
- "f:131:23:131:24": 14,
- "s:132:28:132:Infinity": 80,
- "s:134:8:134:Infinity": 81,
- "f:134:17:134:18": 15,
- "s:134:32:134:92": 82,
- "f:134:46:134:47": 16,
- "s:134:54:134:91": 83,
- "b:134:75:134:89:134:89:134:91": 20,
- "s:136:8:140:Infinity": 84,
- "f:140:17:140:18": 17,
- "s:140:26:140:67": 85,
- "s:143:23:153:Infinity": 86,
- "f:143:23:143:24": 18,
- "s:144:28:144:Infinity": 87,
- "s:146:8:146:Infinity": 88,
- "f:146:17:146:18": 19,
- "s:146:32:146:92": 89,
- "f:146:46:146:47": 20,
- "s:146:54:146:91": 90,
- "b:146:75:146:89:146:89:146:91": 21,
- "s:148:8:152:Infinity": 91,
- "f:152:17:152:18": 21,
- "s:152:26:152:69": 92,
- "s:155:4:191:Infinity": 93,
- "f:155:14:155:20": 22,
- "s:156:25:180:Infinity": 94,
- "f:157:12:157:13": 23,
- "s:158:16:177:Infinity": 95,
- "f:158:32:158:33": 24,
- "b:160:20:165:Infinity:undefined:undefined:undefined:undefined": 22,
- "s:160:20:165:Infinity": 96,
- "b:161:24:163:Infinity:undefined:undefined:undefined:undefined": 23,
- "s:161:24:163:Infinity": 97,
- "b:161:28:161:52:161:52:161:68:161:68:161:79:161:79:161:97": 24,
- "s:162:28:162:Infinity": 98,
- "s:164:24:164:Infinity": 99,
- "b:168:20:176:Infinity:undefined:undefined:undefined:undefined": 25,
- "s:168:20:176:Infinity": 100,
- "b:168:24:168:49:168:49:168:83": 26,
- "s:169:38:169:Infinity": 101,
- "b:170:24:175:Infinity:undefined:undefined:undefined:undefined": 27,
- "s:170:24:175:Infinity": 102,
- "b:170:28:170:45:170:45:170:59:170:59:170:81": 28,
- "s:171:41:171:Infinity": 103,
- "b:172:28:174:Infinity:undefined:undefined:undefined:undefined": 29,
- "s:172:28:174:Infinity": 104,
- "s:173:32:173:Infinity": 105,
- "s:182:8:185:Infinity": 106,
- "f:182:22:182:23": 25,
- "s:183:23:183:Infinity": 107,
- "b:184:12:184:Infinity:undefined:undefined:undefined:undefined": 30,
- "s:184:12:184:Infinity": 108,
- "s:184:20:184:Infinity": 109,
- "s:187:25:187:Infinity": 110,
- "b:188:8:188:Infinity:undefined:undefined:undefined:undefined": 31,
- "s:188:8:188:Infinity": 111,
- "s:188:22:188:Infinity": 112,
- "s:190:8:190:Infinity": 113,
- "f:190:15:190:21": 26,
- "s:190:21:190:Infinity": 114,
- "b:193:4:193:Infinity:undefined:undefined:undefined:undefined": 32,
- "s:193:4:193:Infinity": 115,
- "s:193:17:193:Infinity": 116,
- "b:194:4:194:Infinity:undefined:undefined:undefined:undefined": 33,
- "s:194:4:194:Infinity": 117,
- "s:194:15:194:Infinity": 118,
- "s:197:4:221:Infinity": 119,
- "b:200:16:200:Infinity:202:16:219:Infinity": 34,
- "f:203:31:203:32": 27,
- "s:204:24:212:Infinity": 120,
- "b:208:65:208:93:208:93:208:Infinity": 35,
- "f:209:37:209:43": 28,
- "s:209:43:209:Infinity": 121,
- "b:214:21:214:Infinity:215:24:217:Infinity": 36,
- "b:216:43:216:63:216:63:216:Infinity": 37
- }
- }
- },
- "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.css": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.css",
- "statementMap": {},
- "fnMap": {},
- "branchMap": {},
- "s": {},
- "f": {},
- "b": {},
- "meta": { "lastBranch": 0, "lastFunction": 0, "lastStatement": 0, "seen": {} }
- },
- "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.tsx": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.tsx",
- "statementMap": {
- "0": { "start": { "line": 7, "column": 26 }, "end": { "line": 7, "column": null } },
- "1": { "start": { "line": 8, "column": 24 }, "end": { "line": 8, "column": null } },
- "2": { "start": { "line": 9, "column": 30 }, "end": { "line": 9, "column": null } },
- "3": { "start": { "line": 10, "column": 26 }, "end": { "line": 10, "column": null } },
- "4": { "start": { "line": 12, "column": 4 }, "end": { "line": 32, "column": null } },
- "5": { "start": { "line": 13, "column": 8 }, "end": { "line": 31, "column": null } },
- "6": { "start": { "line": 15, "column": 16 }, "end": { "line": 15, "column": null } },
- "7": { "start": { "line": 15, "column": 29 }, "end": { "line": 15, "column": null } },
- "8": { "start": { "line": 16, "column": 16 }, "end": { "line": 16, "column": null } },
- "9": { "start": { "line": 19, "column": 16 }, "end": { "line": 19, "column": null } },
- "10": { "start": { "line": 19, "column": 29 }, "end": { "line": 19, "column": null } },
- "11": { "start": { "line": 20, "column": 16 }, "end": { "line": 20, "column": null } },
- "12": { "start": { "line": 24, "column": 16 }, "end": { "line": 24, "column": null } },
- "13": { "start": { "line": 25, "column": 16 }, "end": { "line": 25, "column": null } },
- "14": { "start": { "line": 26, "column": 16 }, "end": { "line": 26, "column": null } },
- "15": { "start": { "line": 29, "column": 16 }, "end": { "line": 29, "column": null } },
- "16": { "start": { "line": 30, "column": 16 }, "end": { "line": 30, "column": null } },
- "17": { "start": { "line": 34, "column": 4 }, "end": { "line": 34, "column": null } },
- "18": { "start": { "line": 34, "column": 17 }, "end": { "line": 34, "column": null } },
- "19": { "start": { "line": 35, "column": 4 }, "end": { "line": 35, "column": null } },
- "20": { "start": { "line": 35, "column": 15 }, "end": { "line": 35, "column": null } },
- "21": { "start": { "line": 37, "column": 4 }, "end": { "line": 78, "column": null } },
- "22": { "start": { "line": 53, "column": 28 }, "end": { "line": 58, "column": null } },
- "23": { "start": { "line": 69, "column": 28 }, "end": { "line": 73, "column": null } }
- },
- "fnMap": {
- "0": {
- "name": "FeedList",
- "decl": { "start": { "line": 6, "column": 24 }, "end": { "line": 6, "column": 35 } },
- "loc": { "start": { "line": 6, "column": 35 }, "end": { "line": 80, "column": null } },
- "line": 6
- },
- "1": {
- "name": "(anonymous_1)",
- "decl": { "start": { "line": 12, "column": 14 }, "end": { "line": 12, "column": 20 } },
- "loc": { "start": { "line": 12, "column": 20 }, "end": { "line": 32, "column": 7 } },
- "line": 12
- },
- "2": {
- "name": "(anonymous_2)",
- "decl": { "start": { "line": 14, "column": 37 }, "end": { "line": 14, "column": 44 } },
- "loc": { "start": { "line": 14, "column": 44 }, "end": { "line": 17, "column": 13 } },
- "line": 14
- },
- "3": {
- "name": "(anonymous_3)",
- "decl": { "start": { "line": 18, "column": 35 }, "end": { "line": 18, "column": 42 } },
- "loc": { "start": { "line": 18, "column": 42 }, "end": { "line": 21, "column": 13 } },
- "line": 18
- },
- "4": {
- "name": "(anonymous_4)",
- "decl": { "start": { "line": 23, "column": 18 }, "end": { "line": 23, "column": 19 } },
- "loc": { "start": { "line": 23, "column": 45 }, "end": { "line": 27, "column": 13 } },
- "line": 23
- },
- "5": {
- "name": "(anonymous_5)",
- "decl": { "start": { "line": 28, "column": 19 }, "end": { "line": 28, "column": 20 } },
- "loc": { "start": { "line": 28, "column": 28 }, "end": { "line": 31, "column": 13 } },
- "line": 28
- },
- "6": {
- "name": "(anonymous_6)",
- "decl": { "start": { "line": 52, "column": 35 }, "end": { "line": 52, "column": 36 } },
- "loc": { "start": { "line": 53, "column": 28 }, "end": { "line": 58, "column": null } },
- "line": 53
- },
- "7": {
- "name": "(anonymous_7)",
- "decl": { "start": { "line": 68, "column": 34 }, "end": { "line": 68, "column": 35 } },
- "loc": { "start": { "line": 69, "column": 28 }, "end": { "line": 73, "column": null } },
- "line": 69
- }
- },
- "branchMap": {
- "0": {
- "loc": { "start": { "line": 15, "column": 16 }, "end": { "line": 15, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 15, "column": 16 }, "end": { "line": 15, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 15
- },
- "1": {
- "loc": { "start": { "line": 19, "column": 16 }, "end": { "line": 19, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 19, "column": 16 }, "end": { "line": 19, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 19
- },
- "2": {
- "loc": { "start": { "line": 34, "column": 4 }, "end": { "line": 34, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 34, "column": 4 }, "end": { "line": 34, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 34
- },
- "3": {
- "loc": { "start": { "line": 35, "column": 4 }, "end": { "line": 35, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 35, "column": 4 }, "end": { "line": 35, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 35
- },
- "4": {
- "loc": { "start": { "line": 48, "column": 17 }, "end": { "line": 60, "column": null } },
- "type": "cond-expr",
- "locations": [
- { "start": { "line": 49, "column": 20 }, "end": { "line": 49, "column": null } },
- { "start": { "line": 51, "column": 20 }, "end": { "line": 60, "column": null } }
- ],
- "line": 48
- },
- "5": {
- "loc": { "start": { "line": 55, "column": 37 }, "end": { "line": 55, "column": null } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 55, "column": 37 }, "end": { "line": 55, "column": 51 } },
- { "start": { "line": 55, "column": 51 }, "end": { "line": 55, "column": null } }
- ],
- "line": 55
- },
- "6": {
- "loc": { "start": { "line": 57, "column": 33 }, "end": { "line": 57, "column": null } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 57, "column": 33 }, "end": { "line": 57, "column": 50 } },
- { "start": { "line": 57, "column": 50 }, "end": { "line": 57, "column": null } }
- ],
- "line": 57
- },
- "7": {
- "loc": { "start": { "line": 64, "column": 13 }, "end": { "line": 76, "column": null } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 64, "column": 13 }, "end": { "line": 64, "column": 21 } },
- { "start": { "line": 64, "column": 21 }, "end": { "line": 64, "column": null } },
- { "start": { "line": 65, "column": 16 }, "end": { "line": 76, "column": null } }
- ],
- "line": 64
- }
- },
- "s": {
- "0": 11,
- "1": 11,
- "2": 11,
- "3": 11,
- "4": 11,
- "5": 6,
- "6": 4,
- "7": 0,
- "8": 4,
- "9": 4,
- "10": 0,
- "11": 4,
- "12": 4,
- "13": 4,
- "14": 4,
- "15": 1,
- "16": 1,
- "17": 11,
- "18": 6,
- "19": 5,
- "20": 5,
- "21": 4,
- "22": 3,
- "23": 3
- },
- "f": { "0": 11, "1": 6, "2": 4, "3": 4, "4": 4, "5": 1, "6": 3, "7": 3 },
- "b": {
- "0": [0, 4],
- "1": [0, 4],
- "2": [6, 5],
- "3": [1, 4],
- "4": [2, 2],
- "5": [3, 0],
- "6": [3, 3],
- "7": [11, 4, 2]
- },
- "meta": {
- "lastBranch": 8,
- "lastFunction": 8,
- "lastStatement": 24,
- "seen": {
- "f:6:24:6:35": 0,
- "s:7:26:7:Infinity": 0,
- "s:8:24:8:Infinity": 1,
- "s:9:30:9:Infinity": 2,
- "s:10:26:10:Infinity": 3,
- "s:12:4:32:Infinity": 4,
- "f:12:14:12:20": 1,
- "s:13:8:31:Infinity": 5,
- "f:14:37:14:44": 2,
- "b:15:16:15:Infinity:undefined:undefined:undefined:undefined": 0,
- "s:15:16:15:Infinity": 6,
- "s:15:29:15:Infinity": 7,
- "s:16:16:16:Infinity": 8,
- "f:18:35:18:42": 3,
- "b:19:16:19:Infinity:undefined:undefined:undefined:undefined": 1,
- "s:19:16:19:Infinity": 9,
- "s:19:29:19:Infinity": 10,
- "s:20:16:20:Infinity": 11,
- "f:23:18:23:19": 4,
- "s:24:16:24:Infinity": 12,
- "s:25:16:25:Infinity": 13,
- "s:26:16:26:Infinity": 14,
- "f:28:19:28:20": 5,
- "s:29:16:29:Infinity": 15,
- "s:30:16:30:Infinity": 16,
- "b:34:4:34:Infinity:undefined:undefined:undefined:undefined": 2,
- "s:34:4:34:Infinity": 17,
- "s:34:17:34:Infinity": 18,
- "b:35:4:35:Infinity:undefined:undefined:undefined:undefined": 3,
- "s:35:4:35:Infinity": 19,
- "s:35:15:35:Infinity": 20,
- "s:37:4:78:Infinity": 21,
- "b:49:20:49:Infinity:51:20:60:Infinity": 4,
- "f:52:35:52:36": 6,
- "s:53:28:58:Infinity": 22,
- "b:55:37:55:51:55:51:55:Infinity": 5,
- "b:57:33:57:50:57:50:57:Infinity": 6,
- "b:64:13:64:21:64:21:64:Infinity:65:16:76:Infinity": 7,
- "f:68:34:68:35": 7,
- "s:69:28:73:Infinity": 23
- }
- }
- },
- "/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.css": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.css",
- "statementMap": {},
- "fnMap": {},
- "branchMap": {},
- "s": {},
- "f": {},
- "b": {},
- "meta": { "lastBranch": 0, "lastFunction": 0, "lastStatement": 0, "seen": {} }
- },
- "/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.tsx": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.tsx",
- "statementMap": {
- "0": { "start": { "line": 6, "column": 32 }, "end": { "line": 6, "column": null } },
- "1": { "start": { "line": 7, "column": 26 }, "end": { "line": 7, "column": null } },
- "2": { "start": { "line": 8, "column": 10 }, "end": { "line": 8, "column": null } },
- "3": { "start": { "line": 10, "column": 25 }, "end": { "line": 33, "column": null } },
- "4": { "start": { "line": 11, "column": 8 }, "end": { "line": 11, "column": null } },
- "5": { "start": { "line": 12, "column": 8 }, "end": { "line": 12, "column": null } },
- "6": { "start": { "line": 14, "column": 8 }, "end": { "line": 32, "column": null } },
- "7": { "start": { "line": 16, "column": 27 }, "end": { "line": 16, "column": null } },
- "8": { "start": { "line": 17, "column": 12 }, "end": { "line": 17, "column": null } },
- "9": { "start": { "line": 19, "column": 24 }, "end": { "line": 22, "column": null } },
- "10": { "start": { "line": 24, "column": 12 }, "end": { "line": 29, "column": null } },
- "11": { "start": { "line": 25, "column": 16 }, "end": { "line": 25, "column": null } },
- "12": { "start": { "line": 27, "column": 29 }, "end": { "line": 27, "column": null } },
- "13": { "start": { "line": 28, "column": 16 }, "end": { "line": 28, "column": null } },
- "14": { "start": { "line": 31, "column": 12 }, "end": { "line": 31, "column": null } },
- "15": { "start": { "line": 35, "column": 4 }, "end": { "line": 52, "column": null } },
- "16": { "start": { "line": 45, "column": 41 }, "end": { "line": 45, "column": null } }
- },
- "fnMap": {
- "0": {
- "name": "Login",
- "decl": { "start": { "line": 5, "column": 24 }, "end": { "line": 5, "column": 32 } },
- "loc": { "start": { "line": 5, "column": 32 }, "end": { "line": 54, "column": null } },
- "line": 5
- },
- "1": {
- "name": "(anonymous_1)",
- "decl": { "start": { "line": 10, "column": 25 }, "end": { "line": 10, "column": 32 } },
- "loc": { "start": { "line": 10, "column": 49 }, "end": { "line": 33, "column": null } },
- "line": 10
- },
- "2": {
- "name": "(anonymous_2)",
- "decl": { "start": { "line": 45, "column": 34 }, "end": { "line": 45, "column": 35 } },
- "loc": { "start": { "line": 45, "column": 41 }, "end": { "line": 45, "column": null } },
- "line": 45
- }
- },
- "branchMap": {
- "0": {
- "loc": { "start": { "line": 24, "column": 12 }, "end": { "line": 29, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 24, "column": 12 }, "end": { "line": 29, "column": null } },
- { "start": { "line": 26, "column": 19 }, "end": { "line": 29, "column": null } }
- ],
- "line": 24
- },
- "1": {
- "loc": { "start": { "line": 28, "column": 25 }, "end": { "line": 28, "column": 55 } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 28, "column": 25 }, "end": { "line": 28, "column": 41 } },
- { "start": { "line": 28, "column": 41 }, "end": { "line": 28, "column": 55 } }
- ],
- "line": 28
- },
- "2": {
- "loc": { "start": { "line": 49, "column": 17 }, "end": { "line": 49, "column": null } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 49, "column": 17 }, "end": { "line": 49, "column": 26 } },
- { "start": { "line": 49, "column": 26 }, "end": { "line": 49, "column": null } }
- ],
- "line": 49
- }
- },
- "s": {
- "0": 14,
- "1": 14,
- "2": 14,
- "3": 14,
- "4": 3,
- "5": 3,
- "6": 3,
- "7": 3,
- "8": 3,
- "9": 3,
- "10": 2,
- "11": 1,
- "12": 1,
- "13": 1,
- "14": 1,
- "15": 14,
- "16": 3
- },
- "f": { "0": 14, "1": 3, "2": 3 },
- "b": { "0": [1, 1], "1": [1, 0], "2": [14, 2] },
- "meta": {
- "lastBranch": 3,
- "lastFunction": 3,
- "lastStatement": 17,
- "seen": {
- "f:5:24:5:32": 0,
- "s:6:32:6:Infinity": 0,
- "s:7:26:7:Infinity": 1,
- "s:8:10:8:Infinity": 2,
- "s:10:25:33:Infinity": 3,
- "f:10:25:10:32": 1,
- "s:11:8:11:Infinity": 4,
- "s:12:8:12:Infinity": 5,
- "s:14:8:32:Infinity": 6,
- "s:16:27:16:Infinity": 7,
- "s:17:12:17:Infinity": 8,
- "s:19:24:22:Infinity": 9,
- "b:24:12:29:Infinity:26:19:29:Infinity": 0,
- "s:24:12:29:Infinity": 10,
- "s:25:16:25:Infinity": 11,
- "s:27:29:27:Infinity": 12,
- "s:28:16:28:Infinity": 13,
- "b:28:25:28:41:28:41:28:55": 1,
- "s:31:12:31:Infinity": 14,
- "s:35:4:52:Infinity": 15,
- "f:45:34:45:35": 2,
- "s:45:41:45:Infinity": 16,
- "b:49:17:49:26:49:26:49:Infinity": 2
- }
- }
- },
- "/Users/adam/workspace/vibecode/neko/frontend/src/components/Settings.css": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/components/Settings.css",
- "statementMap": {},
- "fnMap": {},
- "branchMap": {},
- "s": {},
- "f": {},
- "b": {},
- "meta": { "lastBranch": 0, "lastFunction": 0, "lastStatement": 0, "seen": {} }
- },
- "/Users/adam/workspace/vibecode/neko/frontend/src/components/Settings.tsx": {
- "path": "/Users/adam/workspace/vibecode/neko/frontend/src/components/Settings.tsx",
- "statementMap": {
- "0": { "start": { "line": 6, "column": 26 }, "end": { "line": 6, "column": null } },
- "1": { "start": { "line": 7, "column": 36 }, "end": { "line": 7, "column": null } },
- "2": { "start": { "line": 8, "column": 30 }, "end": { "line": 8, "column": null } },
- "3": { "start": { "line": 9, "column": 26 }, "end": { "line": 9, "column": null } },
- "4": { "start": { "line": 11, "column": 4 }, "end": { "line": 13, "column": null } },
- "5": { "start": { "line": 12, "column": 8 }, "end": { "line": 12, "column": null } },
- "6": { "start": { "line": 15, "column": 23 }, "end": { "line": 30, "column": null } },
- "7": { "start": { "line": 16, "column": 8 }, "end": { "line": 16, "column": null } },
- "8": { "start": { "line": 17, "column": 8 }, "end": { "line": 29, "column": null } },
- "9": { "start": { "line": 19, "column": 16 }, "end": { "line": 19, "column": null } },
- "10": { "start": { "line": 19, "column": 29 }, "end": { "line": 19, "column": null } },
- "11": { "start": { "line": 20, "column": 16 }, "end": { "line": 20, "column": null } },
- "12": { "start": { "line": 23, "column": 16 }, "end": { "line": 23, "column": null } },
- "13": { "start": { "line": 24, "column": 16 }, "end": { "line": 24, "column": null } },
- "14": { "start": { "line": 27, "column": 16 }, "end": { "line": 27, "column": null } },
- "15": { "start": { "line": 28, "column": 16 }, "end": { "line": 28, "column": null } },
- "16": { "start": { "line": 32, "column": 26 }, "end": { "line": 54, "column": null } },
- "17": { "start": { "line": 33, "column": 8 }, "end": { "line": 33, "column": null } },
- "18": { "start": { "line": 34, "column": 8 }, "end": { "line": 34, "column": null } },
- "19": { "start": { "line": 34, "column": 25 }, "end": { "line": 34, "column": null } },
- "20": { "start": { "line": 36, "column": 8 }, "end": { "line": 36, "column": null } },
- "21": { "start": { "line": 37, "column": 8 }, "end": { "line": 53, "column": null } },
- "22": { "start": { "line": 43, "column": 16 }, "end": { "line": 43, "column": null } },
- "23": { "start": { "line": 43, "column": 29 }, "end": { "line": 43, "column": null } },
- "24": { "start": { "line": 44, "column": 16 }, "end": { "line": 44, "column": null } },
- "25": { "start": { "line": 47, "column": 16 }, "end": { "line": 47, "column": null } },
- "26": { "start": { "line": 48, "column": 16 }, "end": { "line": 48, "column": null } },
- "27": { "start": { "line": 51, "column": 16 }, "end": { "line": 51, "column": null } },
- "28": { "start": { "line": 52, "column": 16 }, "end": { "line": 52, "column": null } },
- "29": { "start": { "line": 56, "column": 29 }, "end": { "line": 72, "column": null } },
- "30": { "start": { "line": 57, "column": 8 }, "end": { "line": 57, "column": null } },
- "31": { "start": { "line": 57, "column": 79 }, "end": { "line": 57, "column": null } },
- "32": { "start": { "line": 59, "column": 8 }, "end": { "line": 59, "column": null } },
- "33": { "start": { "line": 60, "column": 8 }, "end": { "line": 71, "column": null } },
- "34": { "start": { "line": 64, "column": 16 }, "end": { "line": 64, "column": null } },
- "35": { "start": { "line": 64, "column": 29 }, "end": { "line": 64, "column": null } },
- "36": { "start": { "line": 65, "column": 16 }, "end": { "line": 65, "column": null } },
- "37": { "start": { "line": 65, "column": 45 }, "end": { "line": 65, "column": 57 } },
- "38": { "start": { "line": 66, "column": 16 }, "end": { "line": 66, "column": null } },
- "39": { "start": { "line": 69, "column": 16 }, "end": { "line": 69, "column": null } },
- "40": { "start": { "line": 70, "column": 16 }, "end": { "line": 70, "column": null } },
- "41": { "start": { "line": 74, "column": 4 }, "end": { "line": 119, "column": null } },
- "42": { "start": { "line": 84, "column": 41 }, "end": { "line": 84, "column": null } },
- "43": { "start": { "line": 102, "column": 24 }, "end": { "line": 115, "column": null } },
- "44": { "start": { "line": 108, "column": 47 }, "end": { "line": 108, "column": null } }
- },
- "fnMap": {
- "0": {
- "name": "Settings",
- "decl": { "start": { "line": 5, "column": 24 }, "end": { "line": 5, "column": 35 } },
- "loc": { "start": { "line": 5, "column": 35 }, "end": { "line": 121, "column": null } },
- "line": 5
- },
- "1": {
- "name": "(anonymous_1)",
- "decl": { "start": { "line": 11, "column": 14 }, "end": { "line": 11, "column": 20 } },
- "loc": { "start": { "line": 11, "column": 20 }, "end": { "line": 13, "column": 7 } },
- "line": 11
- },
- "2": {
- "name": "(anonymous_2)",
- "decl": { "start": { "line": 15, "column": 23 }, "end": { "line": 15, "column": 29 } },
- "loc": { "start": { "line": 15, "column": 29 }, "end": { "line": 30, "column": null } },
- "line": 15
- },
- "3": {
- "name": "(anonymous_3)",
- "decl": { "start": { "line": 18, "column": 18 }, "end": { "line": 18, "column": 19 } },
- "loc": { "start": { "line": 18, "column": 27 }, "end": { "line": 21, "column": 13 } },
- "line": 18
- },
- "4": {
- "name": "(anonymous_4)",
- "decl": { "start": { "line": 22, "column": 18 }, "end": { "line": 22, "column": 19 } },
- "loc": { "start": { "line": 22, "column": 28 }, "end": { "line": 25, "column": 13 } },
- "line": 22
- },
- "5": {
- "name": "(anonymous_5)",
- "decl": { "start": { "line": 26, "column": 19 }, "end": { "line": 26, "column": 20 } },
- "loc": { "start": { "line": 26, "column": 28 }, "end": { "line": 29, "column": 13 } },
- "line": 26
- },
- "6": {
- "name": "(anonymous_6)",
- "decl": { "start": { "line": 32, "column": 26 }, "end": { "line": 32, "column": 27 } },
- "loc": { "start": { "line": 32, "column": 50 }, "end": { "line": 54, "column": null } },
- "line": 32
- },
- "7": {
- "name": "(anonymous_7)",
- "decl": { "start": { "line": 42, "column": 18 }, "end": { "line": 42, "column": 19 } },
- "loc": { "start": { "line": 42, "column": 27 }, "end": { "line": 45, "column": 13 } },
- "line": 42
- },
- "8": {
- "name": "(anonymous_8)",
- "decl": { "start": { "line": 46, "column": 18 }, "end": { "line": 46, "column": 24 } },
- "loc": { "start": { "line": 46, "column": 24 }, "end": { "line": 49, "column": 13 } },
- "line": 46
- },
- "9": {
- "name": "(anonymous_9)",
- "decl": { "start": { "line": 50, "column": 19 }, "end": { "line": 50, "column": 20 } },
- "loc": { "start": { "line": 50, "column": 28 }, "end": { "line": 53, "column": 13 } },
- "line": 50
- },
- "10": {
- "name": "(anonymous_10)",
- "decl": { "start": { "line": 56, "column": 29 }, "end": { "line": 56, "column": 30 } },
- "loc": { "start": { "line": 56, "column": 45 }, "end": { "line": 72, "column": null } },
- "line": 56
- },
- "11": {
- "name": "(anonymous_11)",
- "decl": { "start": { "line": 63, "column": 18 }, "end": { "line": 63, "column": 19 } },
- "loc": { "start": { "line": 63, "column": 27 }, "end": { "line": 67, "column": 13 } },
- "line": 63
- },
- "12": {
- "name": "(anonymous_12)",
- "decl": { "start": { "line": 65, "column": 38 }, "end": { "line": 65, "column": 39 } },
- "loc": { "start": { "line": 65, "column": 45 }, "end": { "line": 65, "column": 57 } },
- "line": 65
- },
- "13": {
- "name": "(anonymous_13)",
- "decl": { "start": { "line": 68, "column": 19 }, "end": { "line": 68, "column": 20 } },
- "loc": { "start": { "line": 68, "column": 28 }, "end": { "line": 71, "column": 13 } },
- "line": 68
- },
- "14": {
- "name": "(anonymous_14)",
- "decl": { "start": { "line": 84, "column": 34 }, "end": { "line": 84, "column": 35 } },
- "loc": { "start": { "line": 84, "column": 41 }, "end": { "line": 84, "column": null } },
- "line": 84
- },
- "15": {
- "name": "(anonymous_15)",
- "decl": { "start": { "line": 101, "column": 31 }, "end": { "line": 101, "column": 32 } },
- "loc": { "start": { "line": 102, "column": 24 }, "end": { "line": 115, "column": null } },
- "line": 102
- },
- "16": {
- "name": "(anonymous_16)",
- "decl": { "start": { "line": 108, "column": 41 }, "end": { "line": 108, "column": 47 } },
- "loc": { "start": { "line": 108, "column": 47 }, "end": { "line": 108, "column": null } },
- "line": 108
- }
- },
- "branchMap": {
- "0": {
- "loc": { "start": { "line": 19, "column": 16 }, "end": { "line": 19, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 19, "column": 16 }, "end": { "line": 19, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 19
- },
- "1": {
- "loc": { "start": { "line": 34, "column": 8 }, "end": { "line": 34, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 34, "column": 8 }, "end": { "line": 34, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 34
- },
- "2": {
- "loc": { "start": { "line": 43, "column": 16 }, "end": { "line": 43, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 43, "column": 16 }, "end": { "line": 43, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 43
- },
- "3": {
- "loc": { "start": { "line": 57, "column": 8 }, "end": { "line": 57, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 57, "column": 8 }, "end": { "line": 57, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 57
- },
- "4": {
- "loc": { "start": { "line": 64, "column": 16 }, "end": { "line": 64, "column": null } },
- "type": "if",
- "locations": [
- { "start": { "line": 64, "column": 16 }, "end": { "line": 64, "column": null } },
- { "start": {}, "end": {} }
- ],
- "line": 64
- },
- "5": {
- "loc": { "start": { "line": 94, "column": 17 }, "end": { "line": 94, "column": null } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 94, "column": 17 }, "end": { "line": 94, "column": 26 } },
- { "start": { "line": 94, "column": 26 }, "end": { "line": 94, "column": null } }
- ],
- "line": 94
- },
- "6": {
- "loc": { "start": { "line": 99, "column": 17 }, "end": { "line": 99, "column": null } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 99, "column": 17 }, "end": { "line": 99, "column": 28 } },
- { "start": { "line": 99, "column": 28 }, "end": { "line": 99, "column": null } }
- ],
- "line": 99
- },
- "7": {
- "loc": { "start": { "line": 104, "column": 62 }, "end": { "line": 104, "column": 89 } },
- "type": "binary-expr",
- "locations": [
- { "start": { "line": 104, "column": 62 }, "end": { "line": 104, "column": 76 } },
- { "start": { "line": 104, "column": 76 }, "end": { "line": 104, "column": 89 } }
- ],
- "line": 104
- }
- },
- "s": {
- "0": 14,
- "1": 14,
- "2": 14,
- "3": 14,
- "4": 14,
- "5": 3,
- "6": 14,
- "7": 4,
- "8": 4,
- "9": 4,
- "10": 0,
- "11": 4,
- "12": 4,
- "13": 4,
- "14": 0,
- "15": 0,
- "16": 14,
- "17": 1,
- "18": 1,
- "19": 0,
- "20": 1,
- "21": 1,
- "22": 1,
- "23": 0,
- "24": 1,
- "25": 1,
- "26": 1,
- "27": 0,
- "28": 0,
- "29": 14,
- "30": 1,
- "31": 0,
- "32": 1,
- "33": 1,
- "34": 1,
- "35": 0,
- "36": 1,
- "37": 1,
- "38": 1,
- "39": 0,
- "40": 0,
- "41": 14,
- "42": 1,
- "43": 5,
- "44": 1
- },
- "f": {
- "0": 14,
- "1": 3,
- "2": 4,
- "3": 4,
- "4": 4,
- "5": 0,
- "6": 1,
- "7": 1,
- "8": 1,
- "9": 0,
- "10": 1,
- "11": 1,
- "12": 1,
- "13": 0,
- "14": 1,
- "15": 5,
- "16": 1
- },
- "b": {
- "0": [0, 4],
- "1": [0, 1],
- "2": [0, 1],
- "3": [0, 1],
- "4": [0, 1],
- "5": [14, 0],
- "6": [14, 5],
- "7": [5, 0]
- },
- "meta": {
- "lastBranch": 8,
- "lastFunction": 17,
- "lastStatement": 45,
- "seen": {
- "f:5:24:5:35": 0,
- "s:6:26:6:Infinity": 0,
- "s:7:36:7:Infinity": 1,
- "s:8:30:8:Infinity": 2,
- "s:9:26:9:Infinity": 3,
- "s:11:4:13:Infinity": 4,
- "f:11:14:11:20": 1,
- "s:12:8:12:Infinity": 5,
- "s:15:23:30:Infinity": 6,
- "f:15:23:15:29": 2,
- "s:16:8:16:Infinity": 7,
- "s:17:8:29:Infinity": 8,
- "f:18:18:18:19": 3,
- "b:19:16:19:Infinity:undefined:undefined:undefined:undefined": 0,
- "s:19:16:19:Infinity": 9,
- "s:19:29:19:Infinity": 10,
- "s:20:16:20:Infinity": 11,
- "f:22:18:22:19": 4,
- "s:23:16:23:Infinity": 12,
- "s:24:16:24:Infinity": 13,
- "f:26:19:26:20": 5,
- "s:27:16:27:Infinity": 14,
- "s:28:16:28:Infinity": 15,
- "s:32:26:54:Infinity": 16,
- "f:32:26:32:27": 6,
- "s:33:8:33:Infinity": 17,
- "b:34:8:34:Infinity:undefined:undefined:undefined:undefined": 1,
- "s:34:8:34:Infinity": 18,
- "s:34:25:34:Infinity": 19,
- "s:36:8:36:Infinity": 20,
- "s:37:8:53:Infinity": 21,
- "f:42:18:42:19": 7,
- "b:43:16:43:Infinity:undefined:undefined:undefined:undefined": 2,
- "s:43:16:43:Infinity": 22,
- "s:43:29:43:Infinity": 23,
- "s:44:16:44:Infinity": 24,
- "f:46:18:46:24": 8,
- "s:47:16:47:Infinity": 25,
- "s:48:16:48:Infinity": 26,
- "f:50:19:50:20": 9,
- "s:51:16:51:Infinity": 27,
- "s:52:16:52:Infinity": 28,
- "s:56:29:72:Infinity": 29,
- "f:56:29:56:30": 10,
- "b:57:8:57:Infinity:undefined:undefined:undefined:undefined": 3,
- "s:57:8:57:Infinity": 30,
- "s:57:79:57:Infinity": 31,
- "s:59:8:59:Infinity": 32,
- "s:60:8:71:Infinity": 33,
- "f:63:18:63:19": 11,
- "b:64:16:64:Infinity:undefined:undefined:undefined:undefined": 4,
- "s:64:16:64:Infinity": 34,
- "s:64:29:64:Infinity": 35,
- "s:65:16:65:Infinity": 36,
- "f:65:38:65:39": 12,
- "s:65:45:65:57": 37,
- "s:66:16:66:Infinity": 38,
- "f:68:19:68:20": 13,
- "s:69:16:69:Infinity": 39,
- "s:70:16:70:Infinity": 40,
- "s:74:4:119:Infinity": 41,
- "f:84:34:84:35": 14,
- "s:84:41:84:Infinity": 42,
- "b:94:17:94:26:94:26:94:Infinity": 5,
- "b:99:17:99:28:99:28:99:Infinity": 6,
- "f:101:31:101:32": 15,
- "s:102:24:115:Infinity": 43,
- "b:104:62:104:76:104:76:104:89": 7,
- "f:108:41:108:47": 16,
- "s:108:47:108:Infinity": 44
- }
- }
- }
+{"/Users/adam/workspace/vibecode/neko/frontend/src/App.css": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/App.css","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/App.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/App.tsx","statementMap":{"0":{"start":{"line":9,"column":22},"end":{"line":9,"column":null}},"1":{"start":{"line":10,"column":8},"end":{"line":10,"column":null}},"2":{"start":{"line":12,"column":2},"end":{"line":22,"column":null}},"3":{"start":{"line":13,"column":4},"end":{"line":21,"column":null}},"4":{"start":{"line":15,"column":8},"end":{"line":19,"column":null}},"5":{"start":{"line":16,"column":10},"end":{"line":16,"column":null}},"6":{"start":{"line":18,"column":10},"end":{"line":18,"column":null}},"7":{"start":{"line":21,"column":19},"end":{"line":21,"column":33}},"8":{"start":{"line":24,"column":2},"end":{"line":26,"column":null}},"9":{"start":{"line":25,"column":4},"end":{"line":25,"column":null}},"10":{"start":{"line":28,"column":2},"end":{"line":30,"column":null}},"11":{"start":{"line":29,"column":4},"end":{"line":29,"column":null}},"12":{"start":{"line":32,"column":2},"end":{"line":32,"column":null}},"13":{"start":{"line":40,"column":42},"end":{"line":40,"column":null}},"14":{"start":{"line":42,"column":2},"end":{"line":68,"column":null}},"15":{"start":{"line":50,"column":27},"end":{"line":50,"column":null}},"16":{"start":{"line":73,"column":24},"end":{"line":73,"column":null}},"17":{"start":{"line":75,"column":25},"end":{"line":78,"column":null}},"18":{"start":{"line":76,"column":4},"end":{"line":76,"column":null}},"19":{"start":{"line":77,"column":4},"end":{"line":77,"column":null}},"20":{"start":{"line":80,"column":19},"end":{"line":80,"column":null}},"21":{"start":{"line":82,"column":2},"end":{"line":95,"column":null}}},"fnMap":{"0":{"name":"RequireAuth","decl":{"start":{"line":8,"column":9},"end":{"line":8,"column":21}},"loc":{"start":{"line":8,"column":69},"end":{"line":33,"column":null}},"line":8},"1":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":12},"end":{"line":12,"column":18}},"loc":{"start":{"line":12,"column":18},"end":{"line":22,"column":5}},"line":12},"2":{"name":"(anonymous_2)","decl":{"start":{"line":14,"column":12},"end":{"line":14,"column":13}},"loc":{"start":{"line":14,"column":21},"end":{"line":20,"column":7}},"line":14},"3":{"name":"(anonymous_3)","decl":{"start":{"line":21,"column":13},"end":{"line":21,"column":19}},"loc":{"start":{"line":21,"column":19},"end":{"line":21,"column":33}},"line":21},"4":{"name":"Dashboard","decl":{"start":{"line":39,"column":9},"end":{"line":39,"column":19}},"loc":{"start":{"line":39,"column":90},"end":{"line":70,"column":null}},"line":39},"5":{"name":"(anonymous_5)","decl":{"start":{"line":50,"column":21},"end":{"line":50,"column":27}},"loc":{"start":{"line":50,"column":27},"end":{"line":50,"column":null}},"line":50},"6":{"name":"App","decl":{"start":{"line":72,"column":9},"end":{"line":72,"column":15}},"loc":{"start":{"line":72,"column":15},"end":{"line":97,"column":null}},"line":72},"7":{"name":"(anonymous_7)","decl":{"start":{"line":75,"column":25},"end":{"line":75,"column":26}},"loc":{"start":{"line":75,"column":47},"end":{"line":78,"column":null}},"line":75}},"branchMap":{"0":{"loc":{"start":{"line":15,"column":8},"end":{"line":19,"column":null}},"type":"if","locations":[{"start":{"line":15,"column":8},"end":{"line":19,"column":null}},{"start":{"line":17,"column":15},"end":{"line":19,"column":null}}],"line":15},"1":{"loc":{"start":{"line":24,"column":2},"end":{"line":26,"column":null}},"type":"if","locations":[{"start":{"line":24,"column":2},"end":{"line":26,"column":null}},{"start":{},"end":{}}],"line":24},"2":{"loc":{"start":{"line":28,"column":2},"end":{"line":30,"column":null}},"type":"if","locations":[{"start":{"line":28,"column":2},"end":{"line":30,"column":null}},{"start":{},"end":{}}],"line":28},"3":{"loc":{"start":{"line":44,"column":30},"end":{"line":44,"column":83}},"type":"cond-expr","locations":[{"start":{"line":44,"column":47},"end":{"line":44,"column":67}},{"start":{"line":44,"column":67},"end":{"line":44,"column":83}}],"line":44},"4":{"loc":{"start":{"line":47,"column":9},"end":{"line":54,"column":null}},"type":"binary-expr","locations":[{"start":{"line":47,"column":9},"end":{"line":47,"column":null}},{"start":{"line":48,"column":10},"end":{"line":54,"column":null}}],"line":47},"5":{"loc":{"start":{"line":56,"column":47},"end":{"line":56,"column":77}},"type":"cond-expr","locations":[{"start":{"line":56,"column":64},"end":{"line":56,"column":69}},{"start":{"line":56,"column":69},"end":{"line":56,"column":77}}],"line":56},"6":{"loc":{"start":{"line":73,"column":37},"end":{"line":73,"column":82}},"type":"binary-expr","locations":[{"start":{"line":73,"column":37},"end":{"line":73,"column":75}},{"start":{"line":73,"column":75},"end":{"line":73,"column":82}}],"line":73},"7":{"loc":{"start":{"line":80,"column":19},"end":{"line":80,"column":null}},"type":"cond-expr","locations":[{"start":{"line":80,"column":64},"end":{"line":80,"column":72}},{"start":{"line":80,"column":72},"end":{"line":80,"column":null}}],"line":80}},"s":{"0":2,"1":2,"2":2,"3":1,"4":1,"5":1,"6":0,"7":0,"8":2,"9":1,"10":1,"11":0,"12":1,"13":1,"14":1,"15":0,"16":2,"17":2,"18":0,"19":0,"20":2,"21":2},"f":{"0":2,"1":1,"2":1,"3":0,"4":1,"5":0,"6":2,"7":0},"b":{"0":[1,0],"1":[1,1],"2":[0,1],"3":[1,0],"4":[1,0],"5":[1,0],"6":[2,2],"7":[2,0]},"meta":{"lastBranch":8,"lastFunction":8,"lastStatement":22,"seen":{"f:8:9:8:21":0,"s:9:22:9:Infinity":0,"s:10:8:10:Infinity":1,"s:12:2:22:Infinity":2,"f:12:12:12:18":1,"s:13:4:21:Infinity":3,"f:14:12:14:13":2,"b:15:8:19:Infinity:17:15:19:Infinity":0,"s:15:8:19:Infinity":4,"s:16:10:16:Infinity":5,"s:18:10:18:Infinity":6,"f:21:13:21:19":3,"s:21:19:21:33":7,"b:24:2:26:Infinity:undefined:undefined:undefined:undefined":1,"s:24:2:26:Infinity":8,"s:25:4:25:Infinity":9,"b:28:2:30:Infinity:undefined:undefined:undefined:undefined":2,"s:28:2:30:Infinity":10,"s:29:4:29:Infinity":11,"s:32:2:32:Infinity":12,"f:39:9:39:19":4,"s:40:42:40:Infinity":13,"s:42:2:68:Infinity":14,"b:44:47:44:67:44:67:44:83":3,"b:47:9:47:Infinity:48:10:54:Infinity":4,"f:50:21:50:27":5,"s:50:27:50:Infinity":15,"b:56:64:56:69:56:69:56:77":5,"f:72:9:72:15":6,"s:73:24:73:Infinity":16,"b:73:37:73:75:73:75:73:82":6,"s:75:25:78:Infinity":17,"f:75:25:75:26":7,"s:76:4:76:Infinity":18,"s:77:4:77:Infinity":19,"s:80:19:80:Infinity":20,"b:80:64:80:72:80:72:80:Infinity":7,"s:82:2:95:Infinity":21}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/utils.ts": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/utils.ts","statementMap":{"0":{"start":{"line":2,"column":18},"end":{"line":2,"column":null}},"1":{"start":{"line":3,"column":18},"end":{"line":3,"column":null}},"2":{"start":{"line":4,"column":4},"end":{"line":4,"column":null}},"3":{"start":{"line":4,"column":28},"end":{"line":4,"column":null}},"4":{"start":{"line":12,"column":19},"end":{"line":12,"column":null}},"5":{"start":{"line":13,"column":28},"end":{"line":13,"column":null}},"6":{"start":{"line":15,"column":20},"end":{"line":15,"column":null}},"7":{"start":{"line":17,"column":4},"end":{"line":22,"column":null}},"8":{"start":{"line":18,"column":22},"end":{"line":18,"column":null}},"9":{"start":{"line":19,"column":8},"end":{"line":21,"column":null}},"10":{"start":{"line":20,"column":12},"end":{"line":20,"column":null}},"11":{"start":{"line":27,"column":4},"end":{"line":30,"column":null}}},"fnMap":{"0":{"name":"getCookie","decl":{"start":{"line":1,"column":16},"end":{"line":1,"column":26}},"loc":{"start":{"line":1,"column":60},"end":{"line":5,"column":null}},"line":1},"1":{"name":"apiFetch","decl":{"start":{"line":11,"column":22},"end":{"line":11,"column":31}},"loc":{"start":{"line":11,"column":96},"end":{"line":31,"column":null}},"line":11}},"branchMap":{"0":{"loc":{"start":{"line":4,"column":4},"end":{"line":4,"column":null}},"type":"if","locations":[{"start":{"line":4,"column":4},"end":{"line":4,"column":null}},{"start":{},"end":{}}],"line":4},"1":{"loc":{"start":{"line":12,"column":19},"end":{"line":12,"column":null}},"type":"binary-expr","locations":[{"start":{"line":12,"column":19},"end":{"line":12,"column":50}},{"start":{"line":12,"column":50},"end":{"line":12,"column":null}}],"line":12},"2":{"loc":{"start":{"line":15,"column":32},"end":{"line":15,"column":51}},"type":"binary-expr","locations":[{"start":{"line":15,"column":32},"end":{"line":15,"column":49}},{"start":{"line":15,"column":49},"end":{"line":15,"column":51}}],"line":15},"3":{"loc":{"start":{"line":17,"column":4},"end":{"line":22,"column":null}},"type":"if","locations":[{"start":{"line":17,"column":4},"end":{"line":22,"column":null}},{"start":{},"end":{}}],"line":17},"4":{"loc":{"start":{"line":19,"column":8},"end":{"line":21,"column":null}},"type":"if","locations":[{"start":{"line":19,"column":8},"end":{"line":21,"column":null}},{"start":{},"end":{}}],"line":19}},"s":{"0":10,"1":10,"2":10,"3":0,"4":36,"5":36,"6":36,"7":36,"8":10,"9":10,"10":0,"11":36},"f":{"0":10,"1":36},"b":{"0":[0,10],"1":[36,26],"2":[36,31],"3":[10,26],"4":[0,10]},"meta":{"lastBranch":5,"lastFunction":2,"lastStatement":12,"seen":{"f:1:16:1:26":0,"s:2:18:2:Infinity":0,"s:3:18:3:Infinity":1,"b:4:4:4:Infinity:undefined:undefined:undefined:undefined":0,"s:4:4:4:Infinity":2,"s:4:28:4:Infinity":3,"f:11:22:11:31":1,"s:12:19:12:Infinity":4,"b:12:19:12:50:12:50:12:Infinity":1,"s:13:28:13:Infinity":5,"s:15:20:15:Infinity":6,"b:15:32:15:49:15:49:15:51":2,"b:17:4:22:Infinity:undefined:undefined:undefined:undefined":3,"s:17:4:22:Infinity":7,"s:18:22:18:Infinity":8,"b:19:8:21:Infinity:undefined:undefined:undefined:undefined":4,"s:19:8:21:Infinity":9,"s:20:12:20:Infinity":10,"s:27:4:30:Infinity":11}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItem.css": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItem.css","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItem.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItem.tsx","statementMap":{"0":{"start":{"line":12,"column":22},"end":{"line":12,"column":null}},"1":{"start":{"line":13,"column":28},"end":{"line":13,"column":null}},"2":{"start":{"line":15,"column":2},"end":{"line":17,"column":null}},"3":{"start":{"line":16,"column":4},"end":{"line":16,"column":null}},"4":{"start":{"line":19,"column":21},"end":{"line":21,"column":null}},"5":{"start":{"line":20,"column":4},"end":{"line":20,"column":null}},"6":{"start":{"line":23,"column":21},"end":{"line":57,"column":null}},"7":{"start":{"line":24,"column":4},"end":{"line":24,"column":null}},"8":{"start":{"line":26,"column":25},"end":{"line":26,"column":null}},"9":{"start":{"line":27,"column":4},"end":{"line":27,"column":null}},"10":{"start":{"line":29,"column":4},"end":{"line":56,"column":null}},"11":{"start":{"line":41,"column":8},"end":{"line":43,"column":null}},"12":{"start":{"line":42,"column":10},"end":{"line":42,"column":null}},"13":{"start":{"line":44,"column":8},"end":{"line":44,"column":null}},"14":{"start":{"line":49,"column":8},"end":{"line":49,"column":null}},"15":{"start":{"line":52,"column":8},"end":{"line":52,"column":null}},"16":{"start":{"line":54,"column":8},"end":{"line":54,"column":null}},"17":{"start":{"line":55,"column":8},"end":{"line":55,"column":null}},"18":{"start":{"line":59,"column":26},"end":{"line":75,"column":null}},"19":{"start":{"line":60,"column":4},"end":{"line":60,"column":null}},"20":{"start":{"line":61,"column":4},"end":{"line":61,"column":null}},"21":{"start":{"line":62,"column":4},"end":{"line":74,"column":null}},"22":{"start":{"line":64,"column":8},"end":{"line":64,"column":null}},"23":{"start":{"line":64,"column":21},"end":{"line":64,"column":null}},"24":{"start":{"line":65,"column":8},"end":{"line":65,"column":null}},"25":{"start":{"line":68,"column":8},"end":{"line":68,"column":null}},"26":{"start":{"line":69,"column":8},"end":{"line":69,"column":null}},"27":{"start":{"line":72,"column":8},"end":{"line":72,"column":null}},"28":{"start":{"line":73,"column":8},"end":{"line":73,"column":null}},"29":{"start":{"line":77,"column":2},"end":{"line":113,"column":null}},"30":{"start":{"line":85,"column":12},"end":{"line":85,"column":null}},"31":{"start":{"line":86,"column":12},"end":{"line":86,"column":null}}},"fnMap":{"0":{"name":"FeedItem","decl":{"start":{"line":11,"column":24},"end":{"line":11,"column":33}},"loc":{"start":{"line":11,"column":71},"end":{"line":115,"column":null}},"line":11},"1":{"name":"(anonymous_1)","decl":{"start":{"line":15,"column":12},"end":{"line":15,"column":18}},"loc":{"start":{"line":15,"column":18},"end":{"line":17,"column":5}},"line":15},"2":{"name":"(anonymous_2)","decl":{"start":{"line":19,"column":21},"end":{"line":19,"column":27}},"loc":{"start":{"line":19,"column":27},"end":{"line":21,"column":null}},"line":19},"3":{"name":"(anonymous_3)","decl":{"start":{"line":23,"column":21},"end":{"line":23,"column":22}},"loc":{"start":{"line":23,"column":40},"end":{"line":57,"column":null}},"line":23},"4":{"name":"(anonymous_4)","decl":{"start":{"line":40,"column":12},"end":{"line":40,"column":13}},"loc":{"start":{"line":40,"column":21},"end":{"line":45,"column":7}},"line":40},"5":{"name":"(anonymous_5)","decl":{"start":{"line":46,"column":12},"end":{"line":46,"column":18}},"loc":{"start":{"line":46,"column":18},"end":{"line":50,"column":7}},"line":46},"6":{"name":"(anonymous_6)","decl":{"start":{"line":51,"column":13},"end":{"line":51,"column":14}},"loc":{"start":{"line":51,"column":22},"end":{"line":56,"column":7}},"line":51},"7":{"name":"(anonymous_7)","decl":{"start":{"line":59,"column":26},"end":{"line":59,"column":27}},"loc":{"start":{"line":59,"column":51},"end":{"line":75,"column":null}},"line":59},"8":{"name":"(anonymous_8)","decl":{"start":{"line":63,"column":12},"end":{"line":63,"column":13}},"loc":{"start":{"line":63,"column":21},"end":{"line":66,"column":7}},"line":63},"9":{"name":"(anonymous_9)","decl":{"start":{"line":67,"column":12},"end":{"line":67,"column":13}},"loc":{"start":{"line":67,"column":22},"end":{"line":70,"column":7}},"line":67},"10":{"name":"(anonymous_10)","decl":{"start":{"line":71,"column":13},"end":{"line":71,"column":14}},"loc":{"start":{"line":71,"column":22},"end":{"line":74,"column":7}},"line":71},"11":{"name":"(anonymous_11)","decl":{"start":{"line":84,"column":19},"end":{"line":84,"column":20}},"loc":{"start":{"line":84,"column":26},"end":{"line":87,"column":null}},"line":84}},"branchMap":{"0":{"loc":{"start":{"line":41,"column":8},"end":{"line":43,"column":null}},"type":"if","locations":[{"start":{"line":41,"column":8},"end":{"line":43,"column":null}},{"start":{},"end":{}}],"line":41},"1":{"loc":{"start":{"line":64,"column":8},"end":{"line":64,"column":null}},"type":"if","locations":[{"start":{"line":64,"column":8},"end":{"line":64,"column":null}},{"start":{},"end":{}}],"line":64},"2":{"loc":{"start":{"line":78,"column":32},"end":{"line":78,"column":61}},"type":"cond-expr","locations":[{"start":{"line":78,"column":44},"end":{"line":78,"column":53}},{"start":{"line":78,"column":53},"end":{"line":78,"column":61}}],"line":78},"3":{"loc":{"start":{"line":78,"column":65},"end":{"line":78,"column":89}},"type":"cond-expr","locations":[{"start":{"line":78,"column":75},"end":{"line":78,"column":87}},{"start":{"line":78,"column":87},"end":{"line":78,"column":89}}],"line":78},"4":{"loc":{"start":{"line":81,"column":11},"end":{"line":81,"column":null}},"type":"binary-expr","locations":[{"start":{"line":81,"column":11},"end":{"line":81,"column":25}},{"start":{"line":81,"column":25},"end":{"line":81,"column":null}}],"line":81},"5":{"loc":{"start":{"line":88,"column":33},"end":{"line":88,"column":77}},"type":"cond-expr","locations":[{"start":{"line":88,"column":48},"end":{"line":88,"column":63}},{"start":{"line":88,"column":63},"end":{"line":88,"column":77}}],"line":88},"6":{"loc":{"start":{"line":89,"column":17},"end":{"line":89,"column":null}},"type":"cond-expr","locations":[{"start":{"line":89,"column":32},"end":{"line":89,"column":43}},{"start":{"line":89,"column":43},"end":{"line":89,"column":null}}],"line":89},"7":{"loc":{"start":{"line":97,"column":11},"end":{"line":97,"column":null}},"type":"binary-expr","locations":[{"start":{"line":97,"column":11},"end":{"line":97,"column":30}},{"start":{"line":97,"column":30},"end":{"line":97,"column":null}}],"line":97},"8":{"loc":{"start":{"line":100,"column":11},"end":{"line":103,"column":null}},"type":"binary-expr","locations":[{"start":{"line":100,"column":11},"end":{"line":100,"column":null}},{"start":{"line":101,"column":12},"end":{"line":103,"column":null}}],"line":100},"9":{"loc":{"start":{"line":106,"column":6},"end":{"line":111,"column":null}},"type":"binary-expr","locations":[{"start":{"line":107,"column":8},"end":{"line":107,"column":29}},{"start":{"line":107,"column":29},"end":{"line":107,"column":null}},{"start":{"line":108,"column":8},"end":{"line":111,"column":null}}],"line":106},"10":{"loc":{"start":{"line":110,"column":45},"end":{"line":110,"column":83}},"type":"binary-expr","locations":[{"start":{"line":110,"column":45},"end":{"line":110,"column":66}},{"start":{"line":110,"column":66},"end":{"line":110,"column":83}}],"line":110}},"s":{"0":33,"1":33,"2":33,"3":16,"4":33,"5":1,"6":33,"7":1,"8":1,"9":1,"10":1,"11":1,"12":0,"13":1,"14":1,"15":0,"16":0,"17":0,"18":33,"19":1,"20":1,"21":1,"22":1,"23":0,"24":1,"25":1,"26":1,"27":0,"28":0,"29":33,"30":1,"31":1},"f":{"0":33,"1":16,"2":1,"3":1,"4":1,"5":1,"6":0,"7":1,"8":1,"9":1,"10":0,"11":1},"b":{"0":[0,1],"1":[0,1],"2":[16,17],"3":[4,29],"4":[33,0],"5":[4,29],"6":[4,29],"7":[33,13],"8":[33,32],"9":[33,32,12],"10":[12,11]},"meta":{"lastBranch":11,"lastFunction":12,"lastStatement":32,"seen":{"f:11:24:11:33":0,"s:12:22:12:Infinity":0,"s:13:28:13:Infinity":1,"s:15:2:17:Infinity":2,"f:15:12:15:18":1,"s:16:4:16:Infinity":3,"s:19:21:21:Infinity":4,"f:19:21:19:27":2,"s:20:4:20:Infinity":5,"s:23:21:57:Infinity":6,"f:23:21:23:22":3,"s:24:4:24:Infinity":7,"s:26:25:26:Infinity":8,"s:27:4:27:Infinity":9,"s:29:4:56:Infinity":10,"f:40:12:40:13":4,"b:41:8:43:Infinity:undefined:undefined:undefined:undefined":0,"s:41:8:43:Infinity":11,"s:42:10:42:Infinity":12,"s:44:8:44:Infinity":13,"f:46:12:46:18":5,"s:49:8:49:Infinity":14,"f:51:13:51:14":6,"s:52:8:52:Infinity":15,"s:54:8:54:Infinity":16,"s:55:8:55:Infinity":17,"s:59:26:75:Infinity":18,"f:59:26:59:27":7,"s:60:4:60:Infinity":19,"s:61:4:61:Infinity":20,"s:62:4:74:Infinity":21,"f:63:12:63:13":8,"b:64:8:64:Infinity:undefined:undefined:undefined:undefined":1,"s:64:8:64:Infinity":22,"s:64:21:64:Infinity":23,"s:65:8:65:Infinity":24,"f:67:12:67:13":9,"s:68:8:68:Infinity":25,"s:69:8:69:Infinity":26,"f:71:13:71:14":10,"s:72:8:72:Infinity":27,"s:73:8:73:Infinity":28,"s:77:2:113:Infinity":29,"b:78:44:78:53:78:53:78:61":2,"b:78:75:78:87:78:87:78:89":3,"b:81:11:81:25:81:25:81:Infinity":4,"f:84:19:84:20":11,"s:85:12:85:Infinity":30,"s:86:12:86:Infinity":31,"b:88:48:88:63:88:63:88:77":5,"b:89:32:89:43:89:43:89:Infinity":6,"b:97:11:97:30:97:30:97:Infinity":7,"b:100:11:100:Infinity:101:12:103:Infinity":8,"b:107:8:107:29:107:29:107:Infinity:108:8:111:Infinity":9,"b:110:45:110:66:110:66:110:83":10}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.css": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.css","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedItems.tsx","statementMap":{"0":{"start":{"line":9,"column":26},"end":{"line":9,"column":null}},"1":{"start":{"line":10,"column":21},"end":{"line":10,"column":null}},"2":{"start":{"line":11,"column":19},"end":{"line":11,"column":null}},"3":{"start":{"line":13,"column":24},"end":{"line":13,"column":null}},"4":{"start":{"line":14,"column":28},"end":{"line":14,"column":null}},"5":{"start":{"line":15,"column":36},"end":{"line":15,"column":null}},"6":{"start":{"line":16,"column":28},"end":{"line":16,"column":null}},"7":{"start":{"line":17,"column":24},"end":{"line":17,"column":null}},"8":{"start":{"line":18,"column":40},"end":{"line":18,"column":null}},"9":{"start":{"line":20,"column":21},"end":{"line":87,"column":null}},"10":{"start":{"line":21,"column":4},"end":{"line":26,"column":null}},"11":{"start":{"line":22,"column":6},"end":{"line":22,"column":null}},"12":{"start":{"line":24,"column":6},"end":{"line":24,"column":null}},"13":{"start":{"line":25,"column":6},"end":{"line":25,"column":null}},"14":{"start":{"line":27,"column":4},"end":{"line":27,"column":null}},"15":{"start":{"line":29,"column":14},"end":{"line":29,"column":null}},"16":{"start":{"line":30,"column":19},"end":{"line":30,"column":null}},"17":{"start":{"line":32,"column":4},"end":{"line":36,"column":null}},"18":{"start":{"line":33,"column":6},"end":{"line":33,"column":null}},"19":{"start":{"line":34,"column":4},"end":{"line":36,"column":null}},"20":{"start":{"line":35,"column":6},"end":{"line":35,"column":null}},"21":{"start":{"line":38,"column":4},"end":{"line":40,"column":null}},"22":{"start":{"line":39,"column":6},"end":{"line":39,"column":null}},"23":{"start":{"line":43,"column":24},"end":{"line":43,"column":null}},"24":{"start":{"line":44,"column":4},"end":{"line":46,"column":null}},"25":{"start":{"line":45,"column":6},"end":{"line":45,"column":null}},"26":{"start":{"line":48,"column":4},"end":{"line":58,"column":null}},"27":{"start":{"line":49,"column":6},"end":{"line":49,"column":null}},"28":{"start":{"line":50,"column":4},"end":{"line":58,"column":null}},"29":{"start":{"line":51,"column":6},"end":{"line":51,"column":null}},"30":{"start":{"line":52,"column":6},"end":{"line":52,"column":null}},"31":{"start":{"line":55,"column":6},"end":{"line":57,"column":null}},"32":{"start":{"line":56,"column":8},"end":{"line":56,"column":null}},"33":{"start":{"line":60,"column":24},"end":{"line":60,"column":null}},"34":{"start":{"line":61,"column":4},"end":{"line":63,"column":null}},"35":{"start":{"line":62,"column":6},"end":{"line":62,"column":null}},"36":{"start":{"line":65,"column":4},"end":{"line":86,"column":null}},"37":{"start":{"line":67,"column":8},"end":{"line":69,"column":null}},"38":{"start":{"line":68,"column":10},"end":{"line":68,"column":null}},"39":{"start":{"line":70,"column":8},"end":{"line":70,"column":null}},"40":{"start":{"line":73,"column":8},"end":{"line":77,"column":null}},"41":{"start":{"line":74,"column":10},"end":{"line":74,"column":null}},"42":{"start":{"line":74,"column":29},"end":{"line":74,"column":47}},"43":{"start":{"line":76,"column":10},"end":{"line":76,"column":null}},"44":{"start":{"line":78,"column":8},"end":{"line":78,"column":null}},"45":{"start":{"line":79,"column":8},"end":{"line":79,"column":null}},"46":{"start":{"line":80,"column":8},"end":{"line":80,"column":null}},"47":{"start":{"line":83,"column":8},"end":{"line":83,"column":null}},"48":{"start":{"line":84,"column":8},"end":{"line":84,"column":null}},"49":{"start":{"line":85,"column":8},"end":{"line":85,"column":null}},"50":{"start":{"line":89,"column":2},"end":{"line":92,"column":null}},"51":{"start":{"line":90,"column":4},"end":{"line":90,"column":null}},"52":{"start":{"line":91,"column":4},"end":{"line":91,"column":null}},"53":{"start":{"line":95,"column":23},"end":{"line":100,"column":null}},"54":{"start":{"line":96,"column":20},"end":{"line":96,"column":null}},"55":{"start":{"line":97,"column":4},"end":{"line":99,"column":null}},"56":{"start":{"line":98,"column":6},"end":{"line":98,"column":null}},"57":{"start":{"line":102,"column":21},"end":{"line":112,"column":null}},"58":{"start":{"line":103,"column":24},"end":{"line":103,"column":null}},"59":{"start":{"line":105,"column":4},"end":{"line":105,"column":null}},"60":{"start":{"line":105,"column":28},"end":{"line":105,"column":88}},"61":{"start":{"line":105,"column":50},"end":{"line":105,"column":87}},"62":{"start":{"line":107,"column":4},"end":{"line":111,"column":null}},"63":{"start":{"line":111,"column":22},"end":{"line":111,"column":63}},"64":{"start":{"line":114,"column":21},"end":{"line":124,"column":null}},"65":{"start":{"line":115,"column":24},"end":{"line":115,"column":null}},"66":{"start":{"line":117,"column":4},"end":{"line":117,"column":null}},"67":{"start":{"line":117,"column":28},"end":{"line":117,"column":88}},"68":{"start":{"line":117,"column":50},"end":{"line":117,"column":87}},"69":{"start":{"line":119,"column":4},"end":{"line":123,"column":null}},"70":{"start":{"line":123,"column":22},"end":{"line":123,"column":65}},"71":{"start":{"line":126,"column":2},"end":{"line":162,"column":null}},"72":{"start":{"line":127,"column":26},"end":{"line":158,"column":null}},"73":{"start":{"line":128,"column":6},"end":{"line":128,"column":null}},"74":{"start":{"line":128,"column":30},"end":{"line":128,"column":null}},"75":{"start":{"line":130,"column":6},"end":{"line":157,"column":null}},"76":{"start":{"line":131,"column":8},"end":{"line":141,"column":null}},"77":{"start":{"line":132,"column":28},"end":{"line":132,"column":null}},"78":{"start":{"line":133,"column":10},"end":{"line":139,"column":null}},"79":{"start":{"line":134,"column":25},"end":{"line":134,"column":null}},"80":{"start":{"line":135,"column":12},"end":{"line":137,"column":null}},"81":{"start":{"line":136,"column":14},"end":{"line":136,"column":null}},"82":{"start":{"line":138,"column":12},"end":{"line":138,"column":null}},"83":{"start":{"line":140,"column":10},"end":{"line":140,"column":null}},"84":{"start":{"line":142,"column":6},"end":{"line":157,"column":null}},"85":{"start":{"line":143,"column":8},"end":{"line":149,"column":null}},"86":{"start":{"line":144,"column":28},"end":{"line":144,"column":null}},"87":{"start":{"line":145,"column":10},"end":{"line":147,"column":null}},"88":{"start":{"line":146,"column":12},"end":{"line":146,"column":null}},"89":{"start":{"line":148,"column":10},"end":{"line":148,"column":null}},"90":{"start":{"line":150,"column":6},"end":{"line":157,"column":null}},"91":{"start":{"line":151,"column":8},"end":{"line":156,"column":null}},"92":{"start":{"line":152,"column":10},"end":{"line":154,"column":null}},"93":{"start":{"line":153,"column":12},"end":{"line":153,"column":null}},"94":{"start":{"line":155,"column":10},"end":{"line":155,"column":null}},"95":{"start":{"line":160,"column":4},"end":{"line":160,"column":null}},"96":{"start":{"line":161,"column":4},"end":{"line":161,"column":null}},"97":{"start":{"line":161,"column":17},"end":{"line":161,"column":null}},"98":{"start":{"line":166,"column":2},"end":{"line":202,"column":null}},"99":{"start":{"line":167,"column":21},"end":{"line":191,"column":null}},"100":{"start":{"line":169,"column":8},"end":{"line":188,"column":null}},"101":{"start":{"line":171,"column":10},"end":{"line":176,"column":null}},"102":{"start":{"line":172,"column":12},"end":{"line":174,"column":null}},"103":{"start":{"line":173,"column":14},"end":{"line":173,"column":null}},"104":{"start":{"line":175,"column":12},"end":{"line":175,"column":null}},"105":{"start":{"line":179,"column":10},"end":{"line":187,"column":null}},"106":{"start":{"line":180,"column":26},"end":{"line":180,"column":null}},"107":{"start":{"line":181,"column":12},"end":{"line":186,"column":null}},"108":{"start":{"line":182,"column":27},"end":{"line":182,"column":null}},"109":{"start":{"line":183,"column":14},"end":{"line":185,"column":null}},"110":{"start":{"line":184,"column":16},"end":{"line":184,"column":null}},"111":{"start":{"line":193,"column":4},"end":{"line":196,"column":null}},"112":{"start":{"line":194,"column":17},"end":{"line":194,"column":null}},"113":{"start":{"line":195,"column":6},"end":{"line":195,"column":null}},"114":{"start":{"line":195,"column":14},"end":{"line":195,"column":null}},"115":{"start":{"line":198,"column":21},"end":{"line":198,"column":null}},"116":{"start":{"line":199,"column":4},"end":{"line":199,"column":null}},"117":{"start":{"line":199,"column":18},"end":{"line":199,"column":null}},"118":{"start":{"line":201,"column":4},"end":{"line":201,"column":null}},"119":{"start":{"line":201,"column":17},"end":{"line":201,"column":null}},"120":{"start":{"line":204,"column":2},"end":{"line":204,"column":null}},"121":{"start":{"line":204,"column":15},"end":{"line":204,"column":null}},"122":{"start":{"line":205,"column":2},"end":{"line":205,"column":null}},"123":{"start":{"line":205,"column":13},"end":{"line":205,"column":null}},"124":{"start":{"line":207,"column":2},"end":{"line":231,"column":null}},"125":{"start":{"line":214,"column":12},"end":{"line":222,"column":null}},"126":{"start":{"line":219,"column":29},"end":{"line":219,"column":null}}},"fnMap":{"0":{"name":"FeedItems","decl":{"start":{"line":8,"column":24},"end":{"line":8,"column":36}},"loc":{"start":{"line":8,"column":36},"end":{"line":233,"column":null}},"line":8},"1":{"name":"(anonymous_1)","decl":{"start":{"line":20,"column":21},"end":{"line":20,"column":22}},"loc":{"start":{"line":20,"column":41},"end":{"line":87,"column":null}},"line":20},"2":{"name":"(anonymous_2)","decl":{"start":{"line":66,"column":12},"end":{"line":66,"column":13}},"loc":{"start":{"line":66,"column":21},"end":{"line":71,"column":7}},"line":66},"3":{"name":"(anonymous_3)","decl":{"start":{"line":72,"column":12},"end":{"line":72,"column":13}},"loc":{"start":{"line":72,"column":22},"end":{"line":81,"column":7}},"line":72},"4":{"name":"(anonymous_4)","decl":{"start":{"line":74,"column":19},"end":{"line":74,"column":20}},"loc":{"start":{"line":74,"column":29},"end":{"line":74,"column":47}},"line":74},"5":{"name":"(anonymous_5)","decl":{"start":{"line":82,"column":13},"end":{"line":82,"column":14}},"loc":{"start":{"line":82,"column":22},"end":{"line":86,"column":7}},"line":82},"6":{"name":"(anonymous_6)","decl":{"start":{"line":89,"column":12},"end":{"line":89,"column":18}},"loc":{"start":{"line":89,"column":18},"end":{"line":92,"column":5}},"line":89},"7":{"name":"(anonymous_7)","decl":{"start":{"line":95,"column":23},"end":{"line":95,"column":24}},"loc":{"start":{"line":95,"column":42},"end":{"line":100,"column":null}},"line":95},"8":{"name":"(anonymous_8)","decl":{"start":{"line":102,"column":21},"end":{"line":102,"column":22}},"loc":{"start":{"line":102,"column":37},"end":{"line":112,"column":null}},"line":102},"9":{"name":"(anonymous_9)","decl":{"start":{"line":105,"column":13},"end":{"line":105,"column":14}},"loc":{"start":{"line":105,"column":28},"end":{"line":105,"column":88}},"line":105},"10":{"name":"(anonymous_10)","decl":{"start":{"line":105,"column":42},"end":{"line":105,"column":43}},"loc":{"start":{"line":105,"column":50},"end":{"line":105,"column":87}},"line":105},"11":{"name":"(anonymous_11)","decl":{"start":{"line":111,"column":13},"end":{"line":111,"column":14}},"loc":{"start":{"line":111,"column":22},"end":{"line":111,"column":63}},"line":111},"12":{"name":"(anonymous_12)","decl":{"start":{"line":114,"column":21},"end":{"line":114,"column":22}},"loc":{"start":{"line":114,"column":37},"end":{"line":124,"column":null}},"line":114},"13":{"name":"(anonymous_13)","decl":{"start":{"line":117,"column":13},"end":{"line":117,"column":14}},"loc":{"start":{"line":117,"column":28},"end":{"line":117,"column":88}},"line":117},"14":{"name":"(anonymous_14)","decl":{"start":{"line":117,"column":42},"end":{"line":117,"column":43}},"loc":{"start":{"line":117,"column":50},"end":{"line":117,"column":87}},"line":117},"15":{"name":"(anonymous_15)","decl":{"start":{"line":123,"column":13},"end":{"line":123,"column":14}},"loc":{"start":{"line":123,"column":22},"end":{"line":123,"column":65}},"line":123},"16":{"name":"(anonymous_16)","decl":{"start":{"line":126,"column":12},"end":{"line":126,"column":18}},"loc":{"start":{"line":126,"column":18},"end":{"line":162,"column":5}},"line":126},"17":{"name":"(anonymous_17)","decl":{"start":{"line":127,"column":26},"end":{"line":127,"column":27}},"loc":{"start":{"line":127,"column":48},"end":{"line":158,"column":null}},"line":127},"18":{"name":"(anonymous_18)","decl":{"start":{"line":131,"column":25},"end":{"line":131,"column":26}},"loc":{"start":{"line":131,"column":35},"end":{"line":141,"column":9}},"line":131},"19":{"name":"(anonymous_19)","decl":{"start":{"line":143,"column":25},"end":{"line":143,"column":26}},"loc":{"start":{"line":143,"column":35},"end":{"line":149,"column":9}},"line":143},"20":{"name":"(anonymous_20)","decl":{"start":{"line":151,"column":25},"end":{"line":151,"column":26}},"loc":{"start":{"line":151,"column":43},"end":{"line":156,"column":9}},"line":151},"21":{"name":"(anonymous_21)","decl":{"start":{"line":161,"column":11},"end":{"line":161,"column":17}},"loc":{"start":{"line":161,"column":17},"end":{"line":161,"column":null}},"line":161},"22":{"name":"(anonymous_22)","decl":{"start":{"line":166,"column":12},"end":{"line":166,"column":18}},"loc":{"start":{"line":166,"column":18},"end":{"line":202,"column":5}},"line":166},"23":{"name":"(anonymous_23)","decl":{"start":{"line":168,"column":6},"end":{"line":168,"column":7}},"loc":{"start":{"line":168,"column":19},"end":{"line":189,"column":null}},"line":168},"24":{"name":"(anonymous_24)","decl":{"start":{"line":169,"column":24},"end":{"line":169,"column":25}},"loc":{"start":{"line":169,"column":35},"end":{"line":188,"column":9}},"line":169},"25":{"name":"(anonymous_25)","decl":{"start":{"line":193,"column":18},"end":{"line":193,"column":19}},"loc":{"start":{"line":193,"column":32},"end":{"line":196,"column":5}},"line":193},"26":{"name":"(anonymous_26)","decl":{"start":{"line":201,"column":11},"end":{"line":201,"column":17}},"loc":{"start":{"line":201,"column":17},"end":{"line":201,"column":null}},"line":201},"27":{"name":"(anonymous_27)","decl":{"start":{"line":213,"column":21},"end":{"line":213,"column":22}},"loc":{"start":{"line":214,"column":12},"end":{"line":222,"column":null}},"line":214},"28":{"name":"(anonymous_28)","decl":{"start":{"line":219,"column":23},"end":{"line":219,"column":29}},"loc":{"start":{"line":219,"column":29},"end":{"line":219,"column":null}},"line":219}},"branchMap":{"0":{"loc":{"start":{"line":11,"column":19},"end":{"line":11,"column":null}},"type":"binary-expr","locations":[{"start":{"line":11,"column":19},"end":{"line":11,"column":49}},{"start":{"line":11,"column":49},"end":{"line":11,"column":null}}],"line":11},"1":{"loc":{"start":{"line":21,"column":4},"end":{"line":26,"column":null}},"type":"if","locations":[{"start":{"line":21,"column":4},"end":{"line":26,"column":null}},{"start":{"line":23,"column":11},"end":{"line":26,"column":null}}],"line":21},"2":{"loc":{"start":{"line":32,"column":4},"end":{"line":36,"column":null}},"type":"if","locations":[{"start":{"line":32,"column":4},"end":{"line":36,"column":null}},{"start":{"line":34,"column":4},"end":{"line":36,"column":null}}],"line":32},"3":{"loc":{"start":{"line":34,"column":4},"end":{"line":36,"column":null}},"type":"if","locations":[{"start":{"line":34,"column":4},"end":{"line":36,"column":null}},{"start":{},"end":{}}],"line":34},"4":{"loc":{"start":{"line":38,"column":4},"end":{"line":40,"column":null}},"type":"if","locations":[{"start":{"line":38,"column":4},"end":{"line":40,"column":null}},{"start":{},"end":{}}],"line":38},"5":{"loc":{"start":{"line":44,"column":4},"end":{"line":46,"column":null}},"type":"if","locations":[{"start":{"line":44,"column":4},"end":{"line":46,"column":null}},{"start":{},"end":{}}],"line":44},"6":{"loc":{"start":{"line":48,"column":4},"end":{"line":58,"column":null}},"type":"if","locations":[{"start":{"line":48,"column":4},"end":{"line":58,"column":null}},{"start":{"line":50,"column":4},"end":{"line":58,"column":null}}],"line":48},"7":{"loc":{"start":{"line":50,"column":4},"end":{"line":58,"column":null}},"type":"if","locations":[{"start":{"line":50,"column":4},"end":{"line":58,"column":null}},{"start":{"line":53,"column":11},"end":{"line":58,"column":null}}],"line":50},"8":{"loc":{"start":{"line":55,"column":6},"end":{"line":57,"column":null}},"type":"if","locations":[{"start":{"line":55,"column":6},"end":{"line":57,"column":null}},{"start":{},"end":{}}],"line":55},"9":{"loc":{"start":{"line":61,"column":4},"end":{"line":63,"column":null}},"type":"if","locations":[{"start":{"line":61,"column":4},"end":{"line":63,"column":null}},{"start":{},"end":{}}],"line":61},"10":{"loc":{"start":{"line":67,"column":8},"end":{"line":69,"column":null}},"type":"if","locations":[{"start":{"line":67,"column":8},"end":{"line":69,"column":null}},{"start":{},"end":{}}],"line":67},"11":{"loc":{"start":{"line":73,"column":8},"end":{"line":77,"column":null}},"type":"if","locations":[{"start":{"line":73,"column":8},"end":{"line":77,"column":null}},{"start":{"line":75,"column":15},"end":{"line":77,"column":null}}],"line":73},"12":{"loc":{"start":{"line":97,"column":4},"end":{"line":99,"column":null}},"type":"if","locations":[{"start":{"line":97,"column":4},"end":{"line":99,"column":null}},{"start":{},"end":{}}],"line":97},"13":{"loc":{"start":{"line":105,"column":50},"end":{"line":105,"column":87}},"type":"cond-expr","locations":[{"start":{"line":105,"column":71},"end":{"line":105,"column":85}},{"start":{"line":105,"column":85},"end":{"line":105,"column":87}}],"line":105},"14":{"loc":{"start":{"line":117,"column":50},"end":{"line":117,"column":87}},"type":"cond-expr","locations":[{"start":{"line":117,"column":71},"end":{"line":117,"column":85}},{"start":{"line":117,"column":85},"end":{"line":117,"column":87}}],"line":117},"15":{"loc":{"start":{"line":128,"column":6},"end":{"line":128,"column":null}},"type":"if","locations":[{"start":{"line":128,"column":6},"end":{"line":128,"column":null}},{"start":{},"end":{}}],"line":128},"16":{"loc":{"start":{"line":130,"column":6},"end":{"line":157,"column":null}},"type":"if","locations":[{"start":{"line":130,"column":6},"end":{"line":157,"column":null}},{"start":{"line":142,"column":6},"end":{"line":157,"column":null}}],"line":130},"17":{"loc":{"start":{"line":133,"column":10},"end":{"line":139,"column":null}},"type":"if","locations":[{"start":{"line":133,"column":10},"end":{"line":139,"column":null}},{"start":{},"end":{}}],"line":133},"18":{"loc":{"start":{"line":135,"column":12},"end":{"line":137,"column":null}},"type":"if","locations":[{"start":{"line":135,"column":12},"end":{"line":137,"column":null}},{"start":{},"end":{}}],"line":135},"19":{"loc":{"start":{"line":142,"column":6},"end":{"line":157,"column":null}},"type":"if","locations":[{"start":{"line":142,"column":6},"end":{"line":157,"column":null}},{"start":{"line":150,"column":6},"end":{"line":157,"column":null}}],"line":142},"20":{"loc":{"start":{"line":145,"column":10},"end":{"line":147,"column":null}},"type":"if","locations":[{"start":{"line":145,"column":10},"end":{"line":147,"column":null}},{"start":{},"end":{}}],"line":145},"21":{"loc":{"start":{"line":150,"column":6},"end":{"line":157,"column":null}},"type":"if","locations":[{"start":{"line":150,"column":6},"end":{"line":157,"column":null}},{"start":{},"end":{}}],"line":150},"22":{"loc":{"start":{"line":152,"column":10},"end":{"line":154,"column":null}},"type":"if","locations":[{"start":{"line":152,"column":10},"end":{"line":154,"column":null}},{"start":{},"end":{}}],"line":152},"23":{"loc":{"start":{"line":152,"column":14},"end":{"line":152,"column":64}},"type":"binary-expr","locations":[{"start":{"line":152,"column":14},"end":{"line":152,"column":35}},{"start":{"line":152,"column":35},"end":{"line":152,"column":64}}],"line":152},"24":{"loc":{"start":{"line":171,"column":10},"end":{"line":176,"column":null}},"type":"if","locations":[{"start":{"line":171,"column":10},"end":{"line":176,"column":null}},{"start":{},"end":{}}],"line":171},"25":{"loc":{"start":{"line":172,"column":12},"end":{"line":174,"column":null}},"type":"if","locations":[{"start":{"line":172,"column":12},"end":{"line":174,"column":null}},{"start":{},"end":{}}],"line":172},"26":{"loc":{"start":{"line":172,"column":16},"end":{"line":172,"column":85}},"type":"binary-expr","locations":[{"start":{"line":172,"column":16},"end":{"line":172,"column":40}},{"start":{"line":172,"column":40},"end":{"line":172,"column":56}},{"start":{"line":172,"column":56},"end":{"line":172,"column":67}},{"start":{"line":172,"column":67},"end":{"line":172,"column":85}}],"line":172},"27":{"loc":{"start":{"line":179,"column":10},"end":{"line":187,"column":null}},"type":"if","locations":[{"start":{"line":179,"column":10},"end":{"line":187,"column":null}},{"start":{},"end":{}}],"line":179},"28":{"loc":{"start":{"line":179,"column":14},"end":{"line":179,"column":73}},"type":"binary-expr","locations":[{"start":{"line":179,"column":14},"end":{"line":179,"column":39}},{"start":{"line":179,"column":39},"end":{"line":179,"column":73}}],"line":179},"29":{"loc":{"start":{"line":181,"column":12},"end":{"line":186,"column":null}},"type":"if","locations":[{"start":{"line":181,"column":12},"end":{"line":186,"column":null}},{"start":{},"end":{}}],"line":181},"30":{"loc":{"start":{"line":181,"column":16},"end":{"line":181,"column":69}},"type":"binary-expr","locations":[{"start":{"line":181,"column":16},"end":{"line":181,"column":33}},{"start":{"line":181,"column":33},"end":{"line":181,"column":47}},{"start":{"line":181,"column":47},"end":{"line":181,"column":69}}],"line":181},"31":{"loc":{"start":{"line":183,"column":14},"end":{"line":185,"column":null}},"type":"if","locations":[{"start":{"line":183,"column":14},"end":{"line":185,"column":null}},{"start":{},"end":{}}],"line":183},"32":{"loc":{"start":{"line":195,"column":6},"end":{"line":195,"column":null}},"type":"if","locations":[{"start":{"line":195,"column":6},"end":{"line":195,"column":null}},{"start":{},"end":{}}],"line":195},"33":{"loc":{"start":{"line":199,"column":4},"end":{"line":199,"column":null}},"type":"if","locations":[{"start":{"line":199,"column":4},"end":{"line":199,"column":null}},{"start":{},"end":{}}],"line":199},"34":{"loc":{"start":{"line":204,"column":2},"end":{"line":204,"column":null}},"type":"if","locations":[{"start":{"line":204,"column":2},"end":{"line":204,"column":null}},{"start":{},"end":{}}],"line":204},"35":{"loc":{"start":{"line":205,"column":2},"end":{"line":205,"column":null}},"type":"if","locations":[{"start":{"line":205,"column":2},"end":{"line":205,"column":null}},{"start":{},"end":{}}],"line":205},"36":{"loc":{"start":{"line":209,"column":7},"end":{"line":229,"column":null}},"type":"cond-expr","locations":[{"start":{"line":210,"column":8},"end":{"line":210,"column":null}},{"start":{"line":212,"column":8},"end":{"line":229,"column":null}}],"line":209},"37":{"loc":{"start":{"line":224,"column":11},"end":{"line":227,"column":null}},"type":"binary-expr","locations":[{"start":{"line":224,"column":11},"end":{"line":224,"column":null}},{"start":{"line":225,"column":12},"end":{"line":227,"column":null}}],"line":224},"38":{"loc":{"start":{"line":226,"column":15},"end":{"line":226,"column":null}},"type":"cond-expr","locations":[{"start":{"line":226,"column":29},"end":{"line":226,"column":49}},{"start":{"line":226,"column":49},"end":{"line":226,"column":null}}],"line":226}},"s":{"0":27,"1":27,"2":27,"3":27,"4":27,"5":27,"6":27,"7":27,"8":27,"9":27,"10":8,"11":1,"12":7,"13":7,"14":8,"15":8,"16":8,"17":8,"18":2,"19":6,"20":1,"21":8,"22":1,"23":8,"24":8,"25":0,"26":8,"27":0,"28":8,"29":0,"30":0,"31":8,"32":8,"33":8,"34":8,"35":8,"36":8,"37":7,"38":0,"39":7,"40":6,"41":1,"42":1,"43":5,"44":6,"45":6,"46":6,"47":1,"48":1,"49":1,"50":27,"51":7,"52":7,"53":27,"54":2,"55":2,"56":2,"57":27,"58":2,"59":2,"60":2,"61":3,"62":2,"63":0,"64":27,"65":1,"66":1,"67":1,"68":2,"69":1,"70":0,"71":27,"72":23,"73":3,"74":0,"75":3,"76":2,"77":2,"78":2,"79":2,"80":2,"81":1,"82":2,"83":2,"84":1,"85":0,"86":0,"87":0,"88":0,"89":0,"90":1,"91":1,"92":1,"93":1,"94":1,"95":23,"96":23,"97":23,"98":27,"99":24,"100":2,"101":2,"102":1,"103":1,"104":1,"105":1,"106":1,"107":1,"108":1,"109":1,"110":1,"111":24,"112":15,"113":15,"114":15,"115":24,"116":24,"117":10,"118":24,"119":24,"120":27,"121":13,"122":14,"123":14,"124":13,"125":21,"126":0},"f":{"0":27,"1":8,"2":7,"3":6,"4":1,"5":1,"6":7,"7":2,"8":2,"9":2,"10":3,"11":0,"12":1,"13":1,"14":2,"15":0,"16":23,"17":3,"18":2,"19":0,"20":1,"21":23,"22":24,"23":2,"24":2,"25":15,"26":24,"27":21,"28":0},"b":{"0":[27,27],"1":[1,7],"2":[2,6],"3":[1,5],"4":[1,7],"5":[0,8],"6":[0,8],"7":[0,8],"8":[8,0],"9":[8,0],"10":[0,7],"11":[1,5],"12":[2,0],"13":[2,1],"14":[1,1],"15":[0,3],"16":[2,1],"17":[2,0],"18":[1,1],"19":[0,1],"20":[0,0],"21":[1,0],"22":[1,0],"23":[1,1],"24":[1,1],"25":[1,0],"26":[1,1,1,1],"27":[1,0],"28":[1,1],"29":[1,0],"30":[1,1,1],"31":[1,0],"32":[15,0],"33":[10,14],"34":[13,14],"35":[1,13],"36":[0,13],"37":[13,13],"38":[1,12]},"meta":{"lastBranch":39,"lastFunction":29,"lastStatement":127,"seen":{"f:8:24:8:36":0,"s:9:26:9:Infinity":0,"s:10:21:10:Infinity":1,"s:11:19:11:Infinity":2,"b:11:19:11:49:11:49:11:Infinity":0,"s:13:24:13:Infinity":3,"s:14:28:14:Infinity":4,"s:15:36:15:Infinity":5,"s:16:28:16:Infinity":6,"s:17:24:17:Infinity":7,"s:18:40:18:Infinity":8,"s:20:21:87:Infinity":9,"f:20:21:20:22":1,"b:21:4:26:Infinity:23:11:26:Infinity":1,"s:21:4:26:Infinity":10,"s:22:6:22:Infinity":11,"s:24:6:24:Infinity":12,"s:25:6:25:Infinity":13,"s:27:4:27:Infinity":14,"s:29:14:29:Infinity":15,"s:30:19:30:Infinity":16,"b:32:4:36:Infinity:34:4:36:Infinity":2,"s:32:4:36:Infinity":17,"s:33:6:33:Infinity":18,"b:34:4:36:Infinity:undefined:undefined:undefined:undefined":3,"s:34:4:36:Infinity":19,"s:35:6:35:Infinity":20,"b:38:4:40:Infinity:undefined:undefined:undefined:undefined":4,"s:38:4:40:Infinity":21,"s:39:6:39:Infinity":22,"s:43:24:43:Infinity":23,"b:44:4:46:Infinity:undefined:undefined:undefined:undefined":5,"s:44:4:46:Infinity":24,"s:45:6:45:Infinity":25,"b:48:4:58:Infinity:50:4:58:Infinity":6,"s:48:4:58:Infinity":26,"s:49:6:49:Infinity":27,"b:50:4:58:Infinity:53:11:58:Infinity":7,"s:50:4:58:Infinity":28,"s:51:6:51:Infinity":29,"s:52:6:52:Infinity":30,"b:55:6:57:Infinity:undefined:undefined:undefined:undefined":8,"s:55:6:57:Infinity":31,"s:56:8:56:Infinity":32,"s:60:24:60:Infinity":33,"b:61:4:63:Infinity:undefined:undefined:undefined:undefined":9,"s:61:4:63:Infinity":34,"s:62:6:62:Infinity":35,"s:65:4:86:Infinity":36,"f:66:12:66:13":2,"b:67:8:69:Infinity:undefined:undefined:undefined:undefined":10,"s:67:8:69:Infinity":37,"s:68:10:68:Infinity":38,"s:70:8:70:Infinity":39,"f:72:12:72:13":3,"b:73:8:77:Infinity:75:15:77:Infinity":11,"s:73:8:77:Infinity":40,"s:74:10:74:Infinity":41,"f:74:19:74:20":4,"s:74:29:74:47":42,"s:76:10:76:Infinity":43,"s:78:8:78:Infinity":44,"s:79:8:79:Infinity":45,"s:80:8:80:Infinity":46,"f:82:13:82:14":5,"s:83:8:83:Infinity":47,"s:84:8:84:Infinity":48,"s:85:8:85:Infinity":49,"s:89:2:92:Infinity":50,"f:89:12:89:18":6,"s:90:4:90:Infinity":51,"s:91:4:91:Infinity":52,"s:95:23:100:Infinity":53,"f:95:23:95:24":7,"s:96:20:96:Infinity":54,"b:97:4:99:Infinity:undefined:undefined:undefined:undefined":12,"s:97:4:99:Infinity":55,"s:98:6:98:Infinity":56,"s:102:21:112:Infinity":57,"f:102:21:102:22":8,"s:103:24:103:Infinity":58,"s:105:4:105:Infinity":59,"f:105:13:105:14":9,"s:105:28:105:88":60,"f:105:42:105:43":10,"s:105:50:105:87":61,"b:105:71:105:85:105:85:105:87":13,"s:107:4:111:Infinity":62,"f:111:13:111:14":11,"s:111:22:111:63":63,"s:114:21:124:Infinity":64,"f:114:21:114:22":12,"s:115:24:115:Infinity":65,"s:117:4:117:Infinity":66,"f:117:13:117:14":13,"s:117:28:117:88":67,"f:117:42:117:43":14,"s:117:50:117:87":68,"b:117:71:117:85:117:85:117:87":14,"s:119:4:123:Infinity":69,"f:123:13:123:14":15,"s:123:22:123:65":70,"s:126:2:162:Infinity":71,"f:126:12:126:18":16,"s:127:26:158:Infinity":72,"f:127:26:127:27":17,"b:128:6:128:Infinity:undefined:undefined:undefined:undefined":15,"s:128:6:128:Infinity":73,"s:128:30:128:Infinity":74,"b:130:6:157:Infinity:142:6:157:Infinity":16,"s:130:6:157:Infinity":75,"s:131:8:141:Infinity":76,"f:131:25:131:26":18,"s:132:28:132:Infinity":77,"b:133:10:139:Infinity:undefined:undefined:undefined:undefined":17,"s:133:10:139:Infinity":78,"s:134:25:134:Infinity":79,"b:135:12:137:Infinity:undefined:undefined:undefined:undefined":18,"s:135:12:137:Infinity":80,"s:136:14:136:Infinity":81,"s:138:12:138:Infinity":82,"s:140:10:140:Infinity":83,"b:142:6:157:Infinity:150:6:157:Infinity":19,"s:142:6:157:Infinity":84,"s:143:8:149:Infinity":85,"f:143:25:143:26":19,"s:144:28:144:Infinity":86,"b:145:10:147:Infinity:undefined:undefined:undefined:undefined":20,"s:145:10:147:Infinity":87,"s:146:12:146:Infinity":88,"s:148:10:148:Infinity":89,"b:150:6:157:Infinity:undefined:undefined:undefined:undefined":21,"s:150:6:157:Infinity":90,"s:151:8:156:Infinity":91,"f:151:25:151:26":20,"b:152:10:154:Infinity:undefined:undefined:undefined:undefined":22,"s:152:10:154:Infinity":92,"b:152:14:152:35:152:35:152:64":23,"s:153:12:153:Infinity":93,"s:155:10:155:Infinity":94,"s:160:4:160:Infinity":95,"s:161:4:161:Infinity":96,"f:161:11:161:17":21,"s:161:17:161:Infinity":97,"s:166:2:202:Infinity":98,"f:166:12:166:18":22,"s:167:21:191:Infinity":99,"f:168:6:168:7":23,"s:169:8:188:Infinity":100,"f:169:24:169:25":24,"b:171:10:176:Infinity:undefined:undefined:undefined:undefined":24,"s:171:10:176:Infinity":101,"b:172:12:174:Infinity:undefined:undefined:undefined:undefined":25,"s:172:12:174:Infinity":102,"b:172:16:172:40:172:40:172:56:172:56:172:67:172:67:172:85":26,"s:173:14:173:Infinity":103,"s:175:12:175:Infinity":104,"b:179:10:187:Infinity:undefined:undefined:undefined:undefined":27,"s:179:10:187:Infinity":105,"b:179:14:179:39:179:39:179:73":28,"s:180:26:180:Infinity":106,"b:181:12:186:Infinity:undefined:undefined:undefined:undefined":29,"s:181:12:186:Infinity":107,"b:181:16:181:33:181:33:181:47:181:47:181:69":30,"s:182:27:182:Infinity":108,"b:183:14:185:Infinity:undefined:undefined:undefined:undefined":31,"s:183:14:185:Infinity":109,"s:184:16:184:Infinity":110,"s:193:4:196:Infinity":111,"f:193:18:193:19":25,"s:194:17:194:Infinity":112,"b:195:6:195:Infinity:undefined:undefined:undefined:undefined":32,"s:195:6:195:Infinity":113,"s:195:14:195:Infinity":114,"s:198:21:198:Infinity":115,"b:199:4:199:Infinity:undefined:undefined:undefined:undefined":33,"s:199:4:199:Infinity":116,"s:199:18:199:Infinity":117,"s:201:4:201:Infinity":118,"f:201:11:201:17":26,"s:201:17:201:Infinity":119,"b:204:2:204:Infinity:undefined:undefined:undefined:undefined":34,"s:204:2:204:Infinity":120,"s:204:15:204:Infinity":121,"b:205:2:205:Infinity:undefined:undefined:undefined:undefined":35,"s:205:2:205:Infinity":122,"s:205:13:205:Infinity":123,"s:207:2:231:Infinity":124,"b:210:8:210:Infinity:212:8:229:Infinity":36,"f:213:21:213:22":27,"s:214:12:222:Infinity":125,"f:219:23:219:29":28,"s:219:29:219:Infinity":126,"b:224:11:224:Infinity:225:12:227:Infinity":37,"b:226:29:226:49:226:49:226:Infinity":38}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.css": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.css","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/FeedList.tsx","statementMap":{"0":{"start":{"line":16,"column":24},"end":{"line":16,"column":null}},"1":{"start":{"line":17,"column":22},"end":{"line":17,"column":null}},"2":{"start":{"line":18,"column":28},"end":{"line":18,"column":null}},"3":{"start":{"line":19,"column":24},"end":{"line":19,"column":null}},"4":{"start":{"line":20,"column":40},"end":{"line":20,"column":null}},"5":{"start":{"line":21,"column":36},"end":{"line":21,"column":null}},"6":{"start":{"line":22,"column":8},"end":{"line":22,"column":null}},"7":{"start":{"line":23,"column":21},"end":{"line":23,"column":null}},"8":{"start":{"line":24,"column":8},"end":{"line":24,"column":null}},"9":{"start":{"line":25,"column":26},"end":{"line":25,"column":null}},"10":{"start":{"line":28,"column":4},"end":{"line":29,"column":null}},"11":{"start":{"line":31,"column":23},"end":{"line":36,"column":null}},"12":{"start":{"line":32,"column":4},"end":{"line":32,"column":null}},"13":{"start":{"line":33,"column":4},"end":{"line":35,"column":null}},"14":{"start":{"line":34,"column":6},"end":{"line":34,"column":null}},"15":{"start":{"line":38,"column":22},"end":{"line":40,"column":null}},"16":{"start":{"line":39,"column":4},"end":{"line":39,"column":null}},"17":{"start":{"line":42,"column":2},"end":{"line":62,"column":null}},"18":{"start":{"line":43,"column":4},"end":{"line":61,"column":null}},"19":{"start":{"line":45,"column":8},"end":{"line":45,"column":null}},"20":{"start":{"line":45,"column":21},"end":{"line":45,"column":null}},"21":{"start":{"line":46,"column":8},"end":{"line":46,"column":null}},"22":{"start":{"line":49,"column":8},"end":{"line":49,"column":null}},"23":{"start":{"line":49,"column":21},"end":{"line":49,"column":null}},"24":{"start":{"line":50,"column":8},"end":{"line":50,"column":null}},"25":{"start":{"line":54,"column":8},"end":{"line":54,"column":null}},"26":{"start":{"line":55,"column":8},"end":{"line":55,"column":null}},"27":{"start":{"line":56,"column":8},"end":{"line":56,"column":null}},"28":{"start":{"line":59,"column":8},"end":{"line":59,"column":null}},"29":{"start":{"line":60,"column":8},"end":{"line":60,"column":null}},"30":{"start":{"line":64,"column":2},"end":{"line":64,"column":null}},"31":{"start":{"line":64,"column":15},"end":{"line":64,"column":null}},"32":{"start":{"line":65,"column":2},"end":{"line":65,"column":null}},"33":{"start":{"line":65,"column":13},"end":{"line":65,"column":null}},"34":{"start":{"line":67,"column":23},"end":{"line":69,"column":null}},"35":{"start":{"line":68,"column":4},"end":{"line":68,"column":null}},"36":{"start":{"line":68,"column":60},"end":{"line":68,"column":95}},"37":{"start":{"line":71,"column":2},"end":{"line":183,"column":null}},"38":{"start":{"line":73,"column":42},"end":{"line":73,"column":68}},"39":{"start":{"line":83,"column":29},"end":{"line":83,"column":null}},"40":{"start":{"line":115,"column":12},"end":{"line":122,"column":null}},"41":{"start":{"line":137,"column":16},"end":{"line":144,"column":null}},"42":{"start":{"line":168,"column":27},"end":{"line":168,"column":null}},"43":{"start":{"line":175,"column":27},"end":{"line":175,"column":null}}},"fnMap":{"0":{"name":"FeedList","decl":{"start":{"line":7,"column":24},"end":{"line":7,"column":33}},"loc":{"start":{"line":15,"column":3},"end":{"line":185,"column":null}},"line":15},"1":{"name":"(anonymous_1)","decl":{"start":{"line":31,"column":23},"end":{"line":31,"column":24}},"loc":{"start":{"line":31,"column":47},"end":{"line":36,"column":null}},"line":31},"2":{"name":"(anonymous_2)","decl":{"start":{"line":38,"column":22},"end":{"line":38,"column":28}},"loc":{"start":{"line":38,"column":28},"end":{"line":40,"column":null}},"line":38},"3":{"name":"(anonymous_3)","decl":{"start":{"line":42,"column":12},"end":{"line":42,"column":18}},"loc":{"start":{"line":42,"column":18},"end":{"line":62,"column":5}},"line":42},"4":{"name":"(anonymous_4)","decl":{"start":{"line":44,"column":34},"end":{"line":44,"column":35}},"loc":{"start":{"line":44,"column":43},"end":{"line":47,"column":7}},"line":44},"5":{"name":"(anonymous_5)","decl":{"start":{"line":48,"column":32},"end":{"line":48,"column":33}},"loc":{"start":{"line":48,"column":41},"end":{"line":51,"column":7}},"line":48},"6":{"name":"(anonymous_6)","decl":{"start":{"line":53,"column":12},"end":{"line":53,"column":13}},"loc":{"start":{"line":53,"column":39},"end":{"line":57,"column":7}},"line":53},"7":{"name":"(anonymous_7)","decl":{"start":{"line":58,"column":13},"end":{"line":58,"column":14}},"loc":{"start":{"line":58,"column":22},"end":{"line":61,"column":7}},"line":58},"8":{"name":"(anonymous_8)","decl":{"start":{"line":67,"column":23},"end":{"line":67,"column":29}},"loc":{"start":{"line":67,"column":29},"end":{"line":69,"column":null}},"line":67},"9":{"name":"(anonymous_9)","decl":{"start":{"line":68,"column":53},"end":{"line":68,"column":60}},"loc":{"start":{"line":68,"column":60},"end":{"line":68,"column":95}},"line":68},"10":{"name":"(anonymous_10)","decl":{"start":{"line":73,"column":36},"end":{"line":73,"column":42}},"loc":{"start":{"line":73,"column":42},"end":{"line":73,"column":68}},"line":73},"11":{"name":"(anonymous_11)","decl":{"start":{"line":83,"column":22},"end":{"line":83,"column":23}},"loc":{"start":{"line":83,"column":29},"end":{"line":83,"column":null}},"line":83},"12":{"name":"(anonymous_12)","decl":{"start":{"line":110,"column":21},"end":{"line":110,"column":27}},"loc":{"start":{"line":110,"column":27},"end":{"line":110,"column":32}},"line":110},"13":{"name":"(anonymous_13)","decl":{"start":{"line":114,"column":20},"end":{"line":114,"column":21}},"loc":{"start":{"line":115,"column":12},"end":{"line":122,"column":null}},"line":115},"14":{"name":"(anonymous_14)","decl":{"start":{"line":136,"column":25},"end":{"line":136,"column":26}},"loc":{"start":{"line":137,"column":16},"end":{"line":144,"column":null}},"line":137},"15":{"name":"(anonymous_15)","decl":{"start":{"line":168,"column":21},"end":{"line":168,"column":27}},"loc":{"start":{"line":168,"column":27},"end":{"line":168,"column":null}},"line":168},"16":{"name":"(anonymous_16)","decl":{"start":{"line":175,"column":21},"end":{"line":175,"column":27}},"loc":{"start":{"line":175,"column":27},"end":{"line":175,"column":null}},"line":175}},"branchMap":{"0":{"loc":{"start":{"line":28,"column":4},"end":{"line":29,"column":null}},"type":"binary-expr","locations":[{"start":{"line":28,"column":4},"end":{"line":28,"column":null}},{"start":{"line":29,"column":5},"end":{"line":29,"column":null}}],"line":28},"1":{"loc":{"start":{"line":29,"column":5},"end":{"line":29,"column":null}},"type":"cond-expr","locations":[{"start":{"line":29,"column":56},"end":{"line":29,"column":67}},{"start":{"line":29,"column":67},"end":{"line":29,"column":null}}],"line":29},"2":{"loc":{"start":{"line":29,"column":5},"end":{"line":29,"column":56}},"type":"binary-expr","locations":[{"start":{"line":29,"column":5},"end":{"line":29,"column":34}},{"start":{"line":29,"column":34},"end":{"line":29,"column":45}},{"start":{"line":29,"column":45},"end":{"line":29,"column":56}}],"line":29},"3":{"loc":{"start":{"line":33,"column":4},"end":{"line":35,"column":null}},"type":"if","locations":[{"start":{"line":33,"column":4},"end":{"line":35,"column":null}},{"start":{},"end":{}}],"line":33},"4":{"loc":{"start":{"line":45,"column":8},"end":{"line":45,"column":null}},"type":"if","locations":[{"start":{"line":45,"column":8},"end":{"line":45,"column":null}},{"start":{},"end":{}}],"line":45},"5":{"loc":{"start":{"line":49,"column":8},"end":{"line":49,"column":null}},"type":"if","locations":[{"start":{"line":49,"column":8},"end":{"line":49,"column":null}},{"start":{},"end":{}}],"line":49},"6":{"loc":{"start":{"line":64,"column":2},"end":{"line":64,"column":null}},"type":"if","locations":[{"start":{"line":64,"column":2},"end":{"line":64,"column":null}},{"start":{},"end":{}}],"line":64},"7":{"loc":{"start":{"line":65,"column":2},"end":{"line":65,"column":null}},"type":"if","locations":[{"start":{"line":65,"column":2},"end":{"line":65,"column":null}},{"start":{},"end":{}}],"line":65},"8":{"loc":{"start":{"line":92,"column":50},"end":{"line":92,"column":94}},"type":"cond-expr","locations":[{"start":{"line":92,"column":79},"end":{"line":92,"column":90}},{"start":{"line":92,"column":90},"end":{"line":92,"column":94}}],"line":92},"9":{"loc":{"start":{"line":97,"column":47},"end":{"line":97,"column":88}},"type":"cond-expr","locations":[{"start":{"line":97,"column":73},"end":{"line":97,"column":84}},{"start":{"line":97,"column":84},"end":{"line":97,"column":88}}],"line":97},"10":{"loc":{"start":{"line":102,"column":51},"end":{"line":102,"column":96}},"type":"cond-expr","locations":[{"start":{"line":102,"column":81},"end":{"line":102,"column":92}},{"start":{"line":102,"column":92},"end":{"line":102,"column":96}}],"line":102},"11":{"loc":{"start":{"line":118,"column":39},"end":{"line":118,"column":76}},"type":"cond-expr","locations":[{"start":{"line":118,"column":63},"end":{"line":118,"column":74}},{"start":{"line":118,"column":74},"end":{"line":118,"column":76}}],"line":118},"12":{"loc":{"start":{"line":131,"column":9},"end":{"line":146,"column":null}},"type":"binary-expr","locations":[{"start":{"line":131,"column":9},"end":{"line":131,"column":null}},{"start":{"line":132,"column":11},"end":{"line":146,"column":null}}],"line":131},"13":{"loc":{"start":{"line":132,"column":11},"end":{"line":146,"column":null}},"type":"cond-expr","locations":[{"start":{"line":133,"column":12},"end":{"line":133,"column":null}},{"start":{"line":135,"column":12},"end":{"line":146,"column":null}}],"line":132},"14":{"loc":{"start":{"line":140,"column":45},"end":{"line":140,"column":88}},"type":"cond-expr","locations":[{"start":{"line":140,"column":75},"end":{"line":140,"column":86}},{"start":{"line":140,"column":86},"end":{"line":140,"column":88}}],"line":140},"15":{"loc":{"start":{"line":142,"column":21},"end":{"line":142,"column":null}},"type":"binary-expr","locations":[{"start":{"line":142,"column":21},"end":{"line":142,"column":35}},{"start":{"line":142,"column":35},"end":{"line":142,"column":null}}],"line":142},"16":{"loc":{"start":{"line":169,"column":23},"end":{"line":169,"column":null}},"type":"cond-expr","locations":[{"start":{"line":169,"column":43},"end":{"line":169,"column":54}},{"start":{"line":169,"column":54},"end":{"line":169,"column":null}}],"line":169},"17":{"loc":{"start":{"line":176,"column":23},"end":{"line":176,"column":null}},"type":"cond-expr","locations":[{"start":{"line":176,"column":42},"end":{"line":176,"column":53}},{"start":{"line":176,"column":53},"end":{"line":176,"column":null}}],"line":176}},"s":{"0":13,"1":13,"2":13,"3":13,"4":13,"5":13,"6":13,"7":13,"8":13,"9":13,"10":13,"11":13,"12":0,"13":0,"14":0,"15":13,"16":2,"17":13,"18":6,"19":4,"20":0,"21":4,"22":4,"23":0,"24":4,"25":4,"26":4,"27":4,"28":1,"29":1,"30":13,"31":6,"32":7,"33":7,"34":6,"35":1,"36":1,"37":6,"38":0,"39":0,"40":4,"41":2,"42":0,"43":0},"f":{"0":13,"1":0,"2":2,"3":6,"4":4,"5":4,"6":4,"7":1,"8":1,"9":1,"10":0,"11":0,"12":0,"13":4,"14":2,"15":0,"16":0},"b":{"0":[13,13],"1":[13,0],"2":[13,13,13],"3":[0,0],"4":[0,4],"5":[0,4],"6":[6,7],"7":[1,6],"8":[6,0],"9":[0,6],"10":[0,6],"11":[0,4],"12":[13,2],"13":[1,1],"14":[0,2],"15":[2,0],"16":[6,0],"17":[0,6]},"meta":{"lastBranch":18,"lastFunction":17,"lastStatement":44,"seen":{"f:7:24:7:33":0,"s:16:24:16:Infinity":0,"s:17:22:17:Infinity":1,"s:18:28:18:Infinity":2,"s:19:24:19:Infinity":3,"s:20:40:20:Infinity":4,"s:21:36:21:Infinity":5,"s:22:8:22:Infinity":6,"s:23:21:23:Infinity":7,"s:24:8:24:Infinity":8,"s:25:26:25:Infinity":9,"s:28:4:29:Infinity":10,"b:28:4:28:Infinity:29:5:29:Infinity":0,"b:29:56:29:67:29:67:29:Infinity":1,"b:29:5:29:34:29:34:29:45:29:45:29:56":2,"s:31:23:36:Infinity":11,"f:31:23:31:24":1,"s:32:4:32:Infinity":12,"b:33:4:35:Infinity:undefined:undefined:undefined:undefined":3,"s:33:4:35:Infinity":13,"s:34:6:34:Infinity":14,"s:38:22:40:Infinity":15,"f:38:22:38:28":2,"s:39:4:39:Infinity":16,"s:42:2:62:Infinity":17,"f:42:12:42:18":3,"s:43:4:61:Infinity":18,"f:44:34:44:35":4,"b:45:8:45:Infinity:undefined:undefined:undefined:undefined":4,"s:45:8:45:Infinity":19,"s:45:21:45:Infinity":20,"s:46:8:46:Infinity":21,"f:48:32:48:33":5,"b:49:8:49:Infinity:undefined:undefined:undefined:undefined":5,"s:49:8:49:Infinity":22,"s:49:21:49:Infinity":23,"s:50:8:50:Infinity":24,"f:53:12:53:13":6,"s:54:8:54:Infinity":25,"s:55:8:55:Infinity":26,"s:56:8:56:Infinity":27,"f:58:13:58:14":7,"s:59:8:59:Infinity":28,"s:60:8:60:Infinity":29,"b:64:2:64:Infinity:undefined:undefined:undefined:undefined":6,"s:64:2:64:Infinity":30,"s:64:15:64:Infinity":31,"b:65:2:65:Infinity:undefined:undefined:undefined:undefined":7,"s:65:2:65:Infinity":32,"s:65:13:65:Infinity":33,"s:67:23:69:Infinity":34,"f:67:23:67:29":8,"s:68:4:68:Infinity":35,"f:68:53:68:60":9,"s:68:60:68:95":36,"s:71:2:183:Infinity":37,"f:73:36:73:42":10,"s:73:42:73:68":38,"f:83:22:83:23":11,"s:83:29:83:Infinity":39,"b:92:79:92:90:92:90:92:94":8,"b:97:73:97:84:97:84:97:88":9,"b:102:81:102:92:102:92:102:96":10,"f:110:21:110:27":12,"f:114:20:114:21":13,"s:115:12:122:Infinity":40,"b:118:63:118:74:118:74:118:76":11,"b:131:9:131:Infinity:132:11:146:Infinity":12,"b:133:12:133:Infinity:135:12:146:Infinity":13,"f:136:25:136:26":14,"s:137:16:144:Infinity":41,"b:140:75:140:86:140:86:140:88":14,"b:142:21:142:35:142:35:142:Infinity":15,"f:168:21:168:27":15,"s:168:27:168:Infinity":42,"b:169:43:169:54:169:54:169:Infinity":16,"f:175:21:175:27":16,"s:175:27:175:Infinity":43,"b:176:42:176:53:176:53:176:Infinity":17}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.css": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.css","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/Login.tsx","statementMap":{"0":{"start":{"line":8,"column":30},"end":{"line":8,"column":null}},"1":{"start":{"line":9,"column":24},"end":{"line":9,"column":null}},"2":{"start":{"line":10,"column":8},"end":{"line":10,"column":null}},"3":{"start":{"line":12,"column":23},"end":{"line":35,"column":null}},"4":{"start":{"line":13,"column":4},"end":{"line":13,"column":null}},"5":{"start":{"line":14,"column":4},"end":{"line":14,"column":null}},"6":{"start":{"line":16,"column":4},"end":{"line":34,"column":null}},"7":{"start":{"line":18,"column":21},"end":{"line":18,"column":null}},"8":{"start":{"line":19,"column":6},"end":{"line":19,"column":null}},"9":{"start":{"line":21,"column":18},"end":{"line":24,"column":null}},"10":{"start":{"line":26,"column":6},"end":{"line":31,"column":null}},"11":{"start":{"line":27,"column":8},"end":{"line":27,"column":null}},"12":{"start":{"line":29,"column":21},"end":{"line":29,"column":null}},"13":{"start":{"line":30,"column":8},"end":{"line":30,"column":null}},"14":{"start":{"line":33,"column":6},"end":{"line":33,"column":null}},"15":{"start":{"line":37,"column":2},"end":{"line":54,"column":null}},"16":{"start":{"line":47,"column":29},"end":{"line":47,"column":null}}},"fnMap":{"0":{"name":"Login","decl":{"start":{"line":7,"column":24},"end":{"line":7,"column":32}},"loc":{"start":{"line":7,"column":32},"end":{"line":56,"column":null}},"line":7},"1":{"name":"(anonymous_1)","decl":{"start":{"line":12,"column":23},"end":{"line":12,"column":30}},"loc":{"start":{"line":12,"column":47},"end":{"line":35,"column":null}},"line":12},"2":{"name":"(anonymous_2)","decl":{"start":{"line":47,"column":22},"end":{"line":47,"column":23}},"loc":{"start":{"line":47,"column":29},"end":{"line":47,"column":null}},"line":47}},"branchMap":{"0":{"loc":{"start":{"line":26,"column":6},"end":{"line":31,"column":null}},"type":"if","locations":[{"start":{"line":26,"column":6},"end":{"line":31,"column":null}},{"start":{"line":28,"column":13},"end":{"line":31,"column":null}}],"line":26},"1":{"loc":{"start":{"line":30,"column":17},"end":{"line":30,"column":47}},"type":"binary-expr","locations":[{"start":{"line":30,"column":17},"end":{"line":30,"column":33}},{"start":{"line":30,"column":33},"end":{"line":30,"column":47}}],"line":30},"2":{"loc":{"start":{"line":51,"column":9},"end":{"line":51,"column":null}},"type":"binary-expr","locations":[{"start":{"line":51,"column":9},"end":{"line":51,"column":18}},{"start":{"line":51,"column":18},"end":{"line":51,"column":null}}],"line":51}},"s":{"0":14,"1":14,"2":14,"3":14,"4":3,"5":3,"6":3,"7":3,"8":3,"9":3,"10":2,"11":1,"12":1,"13":1,"14":1,"15":14,"16":3},"f":{"0":14,"1":3,"2":3},"b":{"0":[1,1],"1":[1,0],"2":[14,2]},"meta":{"lastBranch":3,"lastFunction":3,"lastStatement":17,"seen":{"f:7:24:7:32":0,"s:8:30:8:Infinity":0,"s:9:24:9:Infinity":1,"s:10:8:10:Infinity":2,"s:12:23:35:Infinity":3,"f:12:23:12:30":1,"s:13:4:13:Infinity":4,"s:14:4:14:Infinity":5,"s:16:4:34:Infinity":6,"s:18:21:18:Infinity":7,"s:19:6:19:Infinity":8,"s:21:18:24:Infinity":9,"b:26:6:31:Infinity:28:13:31:Infinity":0,"s:26:6:31:Infinity":10,"s:27:8:27:Infinity":11,"s:29:21:29:Infinity":12,"s:30:8:30:Infinity":13,"b:30:17:30:33:30:33:30:47":1,"s:33:6:33:Infinity":14,"s:37:2:54:Infinity":15,"f:47:22:47:23":2,"s:47:29:47:Infinity":16,"b:51:9:51:18:51:18:51:Infinity":2}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/components/Settings.css": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/Settings.css","statementMap":{},"fnMap":{},"branchMap":{},"s":{},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":0,"seen":{}}}
+,"/Users/adam/workspace/vibecode/neko/frontend/src/components/Settings.tsx": {"path":"/Users/adam/workspace/vibecode/neko/frontend/src/components/Settings.tsx","statementMap":{"0":{"start":{"line":7,"column":24},"end":{"line":7,"column":null}},"1":{"start":{"line":8,"column":34},"end":{"line":8,"column":null}},"2":{"start":{"line":9,"column":28},"end":{"line":9,"column":null}},"3":{"start":{"line":10,"column":24},"end":{"line":10,"column":null}},"4":{"start":{"line":12,"column":34},"end":{"line":12,"column":null}},"5":{"start":{"line":14,"column":21},"end":{"line":29,"column":null}},"6":{"start":{"line":15,"column":4},"end":{"line":15,"column":null}},"7":{"start":{"line":16,"column":4},"end":{"line":28,"column":null}},"8":{"start":{"line":18,"column":8},"end":{"line":18,"column":null}},"9":{"start":{"line":18,"column":21},"end":{"line":18,"column":null}},"10":{"start":{"line":19,"column":8},"end":{"line":19,"column":null}},"11":{"start":{"line":22,"column":8},"end":{"line":22,"column":null}},"12":{"start":{"line":23,"column":8},"end":{"line":23,"column":null}},"13":{"start":{"line":26,"column":8},"end":{"line":26,"column":null}},"14":{"start":{"line":27,"column":8},"end":{"line":27,"column":null}},"15":{"start":{"line":31,"column":2},"end":{"line":33,"column":null}},"16":{"start":{"line":32,"column":4},"end":{"line":32,"column":null}},"17":{"start":{"line":35,"column":24},"end":{"line":57,"column":null}},"18":{"start":{"line":36,"column":4},"end":{"line":36,"column":null}},"19":{"start":{"line":37,"column":4},"end":{"line":37,"column":null}},"20":{"start":{"line":37,"column":21},"end":{"line":37,"column":null}},"21":{"start":{"line":39,"column":4},"end":{"line":39,"column":null}},"22":{"start":{"line":40,"column":4},"end":{"line":56,"column":null}},"23":{"start":{"line":46,"column":8},"end":{"line":46,"column":null}},"24":{"start":{"line":46,"column":21},"end":{"line":46,"column":null}},"25":{"start":{"line":47,"column":8},"end":{"line":47,"column":null}},"26":{"start":{"line":50,"column":8},"end":{"line":50,"column":null}},"27":{"start":{"line":51,"column":8},"end":{"line":51,"column":null}},"28":{"start":{"line":54,"column":8},"end":{"line":54,"column":null}},"29":{"start":{"line":55,"column":8},"end":{"line":55,"column":null}},"30":{"start":{"line":59,"column":27},"end":{"line":75,"column":null}},"31":{"start":{"line":60,"column":4},"end":{"line":60,"column":null}},"32":{"start":{"line":60,"column":75},"end":{"line":60,"column":null}},"33":{"start":{"line":62,"column":4},"end":{"line":62,"column":null}},"34":{"start":{"line":63,"column":4},"end":{"line":74,"column":null}},"35":{"start":{"line":67,"column":8},"end":{"line":67,"column":null}},"36":{"start":{"line":67,"column":21},"end":{"line":67,"column":null}},"37":{"start":{"line":68,"column":8},"end":{"line":68,"column":null}},"38":{"start":{"line":68,"column":37},"end":{"line":68,"column":49}},"39":{"start":{"line":69,"column":8},"end":{"line":69,"column":null}},"40":{"start":{"line":72,"column":8},"end":{"line":72,"column":null}},"41":{"start":{"line":73,"column":8},"end":{"line":73,"column":null}},"42":{"start":{"line":77,"column":23},"end":{"line":103,"column":null}},"43":{"start":{"line":78,"column":4},"end":{"line":78,"column":null}},"44":{"start":{"line":79,"column":4},"end":{"line":79,"column":null}},"45":{"start":{"line":79,"column":21},"end":{"line":79,"column":null}},"46":{"start":{"line":81,"column":4},"end":{"line":81,"column":null}},"47":{"start":{"line":82,"column":21},"end":{"line":82,"column":null}},"48":{"start":{"line":83,"column":4},"end":{"line":83,"column":null}},"49":{"start":{"line":84,"column":4},"end":{"line":84,"column":null}},"50":{"start":{"line":86,"column":4},"end":{"line":102,"column":null}},"51":{"start":{"line":91,"column":8},"end":{"line":91,"column":null}},"52":{"start":{"line":91,"column":21},"end":{"line":91,"column":null}},"53":{"start":{"line":92,"column":8},"end":{"line":92,"column":null}},"54":{"start":{"line":95,"column":8},"end":{"line":95,"column":null}},"55":{"start":{"line":96,"column":8},"end":{"line":96,"column":null}},"56":{"start":{"line":97,"column":8},"end":{"line":97,"column":null}},"57":{"start":{"line":100,"column":8},"end":{"line":100,"column":null}},"58":{"start":{"line":101,"column":8},"end":{"line":101,"column":null}},"59":{"start":{"line":105,"column":2},"end":{"line":178,"column":null}},"60":{"start":{"line":115,"column":29},"end":{"line":115,"column":null}},"61":{"start":{"line":134,"column":31},"end":{"line":134,"column":null}},"62":{"start":{"line":161,"column":12},"end":{"line":174,"column":null}},"63":{"start":{"line":167,"column":31},"end":{"line":167,"column":null}}},"fnMap":{"0":{"name":"Settings","decl":{"start":{"line":6,"column":24},"end":{"line":6,"column":35}},"loc":{"start":{"line":6,"column":35},"end":{"line":180,"column":null}},"line":6},"1":{"name":"(anonymous_1)","decl":{"start":{"line":14,"column":21},"end":{"line":14,"column":27}},"loc":{"start":{"line":14,"column":27},"end":{"line":29,"column":null}},"line":14},"2":{"name":"(anonymous_2)","decl":{"start":{"line":17,"column":12},"end":{"line":17,"column":13}},"loc":{"start":{"line":17,"column":21},"end":{"line":20,"column":7}},"line":17},"3":{"name":"(anonymous_3)","decl":{"start":{"line":21,"column":12},"end":{"line":21,"column":13}},"loc":{"start":{"line":21,"column":22},"end":{"line":24,"column":7}},"line":21},"4":{"name":"(anonymous_4)","decl":{"start":{"line":25,"column":13},"end":{"line":25,"column":14}},"loc":{"start":{"line":25,"column":22},"end":{"line":28,"column":7}},"line":25},"5":{"name":"(anonymous_5)","decl":{"start":{"line":31,"column":12},"end":{"line":31,"column":18}},"loc":{"start":{"line":31,"column":18},"end":{"line":33,"column":5}},"line":31},"6":{"name":"(anonymous_6)","decl":{"start":{"line":35,"column":24},"end":{"line":35,"column":25}},"loc":{"start":{"line":35,"column":48},"end":{"line":57,"column":null}},"line":35},"7":{"name":"(anonymous_7)","decl":{"start":{"line":45,"column":12},"end":{"line":45,"column":13}},"loc":{"start":{"line":45,"column":21},"end":{"line":48,"column":7}},"line":45},"8":{"name":"(anonymous_8)","decl":{"start":{"line":49,"column":12},"end":{"line":49,"column":18}},"loc":{"start":{"line":49,"column":18},"end":{"line":52,"column":7}},"line":49},"9":{"name":"(anonymous_9)","decl":{"start":{"line":53,"column":13},"end":{"line":53,"column":14}},"loc":{"start":{"line":53,"column":22},"end":{"line":56,"column":7}},"line":53},"10":{"name":"(anonymous_10)","decl":{"start":{"line":59,"column":27},"end":{"line":59,"column":28}},"loc":{"start":{"line":59,"column":43},"end":{"line":75,"column":null}},"line":59},"11":{"name":"(anonymous_11)","decl":{"start":{"line":66,"column":12},"end":{"line":66,"column":13}},"loc":{"start":{"line":66,"column":21},"end":{"line":70,"column":7}},"line":66},"12":{"name":"(anonymous_12)","decl":{"start":{"line":68,"column":30},"end":{"line":68,"column":31}},"loc":{"start":{"line":68,"column":37},"end":{"line":68,"column":49}},"line":68},"13":{"name":"(anonymous_13)","decl":{"start":{"line":71,"column":13},"end":{"line":71,"column":14}},"loc":{"start":{"line":71,"column":22},"end":{"line":74,"column":7}},"line":71},"14":{"name":"(anonymous_14)","decl":{"start":{"line":77,"column":23},"end":{"line":77,"column":24}},"loc":{"start":{"line":77,"column":47},"end":{"line":103,"column":null}},"line":77},"15":{"name":"(anonymous_15)","decl":{"start":{"line":90,"column":12},"end":{"line":90,"column":13}},"loc":{"start":{"line":90,"column":21},"end":{"line":93,"column":7}},"line":90},"16":{"name":"(anonymous_16)","decl":{"start":{"line":94,"column":12},"end":{"line":94,"column":18}},"loc":{"start":{"line":94,"column":18},"end":{"line":98,"column":7}},"line":94},"17":{"name":"(anonymous_17)","decl":{"start":{"line":99,"column":13},"end":{"line":99,"column":14}},"loc":{"start":{"line":99,"column":22},"end":{"line":102,"column":7}},"line":99},"18":{"name":"(anonymous_18)","decl":{"start":{"line":115,"column":22},"end":{"line":115,"column":23}},"loc":{"start":{"line":115,"column":29},"end":{"line":115,"column":null}},"line":115},"19":{"name":"(anonymous_19)","decl":{"start":{"line":134,"column":24},"end":{"line":134,"column":25}},"loc":{"start":{"line":134,"column":31},"end":{"line":134,"column":null}},"line":134},"20":{"name":"(anonymous_20)","decl":{"start":{"line":160,"column":21},"end":{"line":160,"column":22}},"loc":{"start":{"line":161,"column":12},"end":{"line":174,"column":null}},"line":161},"21":{"name":"(anonymous_21)","decl":{"start":{"line":167,"column":25},"end":{"line":167,"column":31}},"loc":{"start":{"line":167,"column":31},"end":{"line":167,"column":null}},"line":167}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":8},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":8},"end":{"line":18,"column":null}},{"start":{},"end":{}}],"line":18},"1":{"loc":{"start":{"line":37,"column":4},"end":{"line":37,"column":null}},"type":"if","locations":[{"start":{"line":37,"column":4},"end":{"line":37,"column":null}},{"start":{},"end":{}}],"line":37},"2":{"loc":{"start":{"line":46,"column":8},"end":{"line":46,"column":null}},"type":"if","locations":[{"start":{"line":46,"column":8},"end":{"line":46,"column":null}},{"start":{},"end":{}}],"line":46},"3":{"loc":{"start":{"line":60,"column":4},"end":{"line":60,"column":null}},"type":"if","locations":[{"start":{"line":60,"column":4},"end":{"line":60,"column":null}},{"start":{},"end":{}}],"line":60},"4":{"loc":{"start":{"line":67,"column":8},"end":{"line":67,"column":null}},"type":"if","locations":[{"start":{"line":67,"column":8},"end":{"line":67,"column":null}},{"start":{},"end":{}}],"line":67},"5":{"loc":{"start":{"line":79,"column":4},"end":{"line":79,"column":null}},"type":"if","locations":[{"start":{"line":79,"column":4},"end":{"line":79,"column":null}},{"start":{},"end":{}}],"line":79},"6":{"loc":{"start":{"line":91,"column":8},"end":{"line":91,"column":null}},"type":"if","locations":[{"start":{"line":91,"column":8},"end":{"line":91,"column":null}},{"start":{},"end":{}}],"line":91},"7":{"loc":{"start":{"line":134,"column":45},"end":{"line":134,"column":72}},"type":"binary-expr","locations":[{"start":{"line":134,"column":45},"end":{"line":134,"column":68}},{"start":{"line":134,"column":68},"end":{"line":134,"column":72}}],"line":134},"8":{"loc":{"start":{"line":138,"column":44},"end":{"line":138,"column":68}},"type":"binary-expr","locations":[{"start":{"line":138,"column":44},"end":{"line":138,"column":59}},{"start":{"line":138,"column":59},"end":{"line":138,"column":68}}],"line":138},"9":{"loc":{"start":{"line":154,"column":7},"end":{"line":154,"column":null}},"type":"binary-expr","locations":[{"start":{"line":154,"column":7},"end":{"line":154,"column":16}},{"start":{"line":154,"column":16},"end":{"line":154,"column":null}}],"line":154},"10":{"loc":{"start":{"line":158,"column":9},"end":{"line":158,"column":null}},"type":"binary-expr","locations":[{"start":{"line":158,"column":9},"end":{"line":158,"column":20}},{"start":{"line":158,"column":20},"end":{"line":158,"column":null}}],"line":158},"11":{"loc":{"start":{"line":163,"column":46},"end":{"line":163,"column":73}},"type":"binary-expr","locations":[{"start":{"line":163,"column":46},"end":{"line":163,"column":60}},{"start":{"line":163,"column":60},"end":{"line":163,"column":73}}],"line":163}},"s":{"0":14,"1":14,"2":14,"3":14,"4":14,"5":14,"6":4,"7":4,"8":4,"9":0,"10":4,"11":4,"12":4,"13":0,"14":0,"15":14,"16":3,"17":14,"18":1,"19":1,"20":0,"21":1,"22":1,"23":1,"24":0,"25":1,"26":1,"27":1,"28":0,"29":0,"30":14,"31":1,"32":0,"33":1,"34":1,"35":1,"36":0,"37":1,"38":1,"39":1,"40":0,"41":0,"42":14,"43":0,"44":0,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":14,"60":1,"61":0,"62":5,"63":1},"f":{"0":14,"1":4,"2":4,"3":4,"4":0,"5":3,"6":1,"7":1,"8":1,"9":0,"10":1,"11":1,"12":1,"13":0,"14":0,"15":0,"16":0,"17":0,"18":1,"19":0,"20":5,"21":1},"b":{"0":[0,4],"1":[0,1],"2":[0,1],"3":[0,1],"4":[0,1],"5":[0,0],"6":[0,0],"7":[0,0],"8":[14,0],"9":[14,0],"10":[14,5],"11":[5,0]},"meta":{"lastBranch":12,"lastFunction":22,"lastStatement":64,"seen":{"f:6:24:6:35":0,"s:7:24:7:Infinity":0,"s:8:34:8:Infinity":1,"s:9:28:9:Infinity":2,"s:10:24:10:Infinity":3,"s:12:34:12:Infinity":4,"s:14:21:29:Infinity":5,"f:14:21:14:27":1,"s:15:4:15:Infinity":6,"s:16:4:28:Infinity":7,"f:17:12:17:13":2,"b:18:8:18:Infinity:undefined:undefined:undefined:undefined":0,"s:18:8:18:Infinity":8,"s:18:21:18:Infinity":9,"s:19:8:19:Infinity":10,"f:21:12:21:13":3,"s:22:8:22:Infinity":11,"s:23:8:23:Infinity":12,"f:25:13:25:14":4,"s:26:8:26:Infinity":13,"s:27:8:27:Infinity":14,"s:31:2:33:Infinity":15,"f:31:12:31:18":5,"s:32:4:32:Infinity":16,"s:35:24:57:Infinity":17,"f:35:24:35:25":6,"s:36:4:36:Infinity":18,"b:37:4:37:Infinity:undefined:undefined:undefined:undefined":1,"s:37:4:37:Infinity":19,"s:37:21:37:Infinity":20,"s:39:4:39:Infinity":21,"s:40:4:56:Infinity":22,"f:45:12:45:13":7,"b:46:8:46:Infinity:undefined:undefined:undefined:undefined":2,"s:46:8:46:Infinity":23,"s:46:21:46:Infinity":24,"s:47:8:47:Infinity":25,"f:49:12:49:18":8,"s:50:8:50:Infinity":26,"s:51:8:51:Infinity":27,"f:53:13:53:14":9,"s:54:8:54:Infinity":28,"s:55:8:55:Infinity":29,"s:59:27:75:Infinity":30,"f:59:27:59:28":10,"b:60:4:60:Infinity:undefined:undefined:undefined:undefined":3,"s:60:4:60:Infinity":31,"s:60:75:60:Infinity":32,"s:62:4:62:Infinity":33,"s:63:4:74:Infinity":34,"f:66:12:66:13":11,"b:67:8:67:Infinity:undefined:undefined:undefined:undefined":4,"s:67:8:67:Infinity":35,"s:67:21:67:Infinity":36,"s:68:8:68:Infinity":37,"f:68:30:68:31":12,"s:68:37:68:49":38,"s:69:8:69:Infinity":39,"f:71:13:71:14":13,"s:72:8:72:Infinity":40,"s:73:8:73:Infinity":41,"s:77:23:103:Infinity":42,"f:77:23:77:24":14,"s:78:4:78:Infinity":43,"b:79:4:79:Infinity:undefined:undefined:undefined:undefined":5,"s:79:4:79:Infinity":44,"s:79:21:79:Infinity":45,"s:81:4:81:Infinity":46,"s:82:21:82:Infinity":47,"s:83:4:83:Infinity":48,"s:84:4:84:Infinity":49,"s:86:4:102:Infinity":50,"f:90:12:90:13":15,"b:91:8:91:Infinity:undefined:undefined:undefined:undefined":6,"s:91:8:91:Infinity":51,"s:91:21:91:Infinity":52,"s:92:8:92:Infinity":53,"f:94:12:94:18":16,"s:95:8:95:Infinity":54,"s:96:8:96:Infinity":55,"s:97:8:97:Infinity":56,"f:99:13:99:14":17,"s:100:8:100:Infinity":57,"s:101:8:101:Infinity":58,"s:105:2:178:Infinity":59,"f:115:22:115:23":18,"s:115:29:115:Infinity":60,"f:134:24:134:25":19,"s:134:31:134:Infinity":61,"b:134:45:134:68:134:68:134:72":7,"b:138:44:138:59:138:59:138:68":8,"b:154:7:154:16:154:16:154:Infinity":9,"b:158:9:158:20:158:20:158:Infinity":10,"f:160:21:160:22":20,"s:161:12:174:Infinity":62,"b:163:46:163:60:163:60:163:73":11,"f:167:25:167:31":21,"s:167:31:167:Infinity":63}}}
}
diff --git a/frontend/coverage/index.html b/frontend/coverage/index.html
index bd1150d..ad1f79d 100644
--- a/frontend/coverage/index.html
+++ b/frontend/coverage/index.html
@@ -1,141 +1,131 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for All files</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 type='text/css'>
+ .coverage-summary .sorter {
+ background-image: url(sort-arrow-sprite.png);
+ }
</style>
- </head>
-
- <body>
- <div class="wrapper">
- <div class="pad1">
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
<h1>All files</h1>
- <div class="clearfix">
- <div class="fl pad1y space-right2">
- <span class="strong">86.17% </span>
- <span class="quiet">Statements</span>
- <span class="fraction">212/246</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">76.76% </span>
- <span class="quiet">Branches</span>
- <span class="fraction">109/142</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">86.3% </span>
- <span class="quiet">Functions</span>
- <span class="fraction">63/73</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">88.39% </span>
- <span class="quiet">Lines</span>
- <span class="fraction">198/224</span>
- </div>
+ <div class='clearfix'>
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">79.24% </span>
+ <span class="quiet">Statements</span>
+ <span class='fraction'>252/318</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">70.05% </span>
+ <span class="quiet">Branches</span>
+ <span class='fraction'>138/197</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">75.26% </span>
+ <span class="quiet">Functions</span>
+ <span class='fraction'>70/93</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">81.44% </span>
+ <span class="quiet">Lines</span>
+ <span class='fraction'>237/291</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line high"></div>
- <div class="pad1">
- <table class="coverage-summary">
- <thead>
- <tr>
- <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
- <th
- data-col="pic"
- data-type="number"
- data-fmt="html"
- data-html="true"
- class="pic"
- ></th>
- <th data-col="statements" data-type="number" data-fmt="pct" class="pct">
- Statements
- </th>
- <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
- <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
- <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
- <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
- <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
- <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
- <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="file medium" data-value="src"><a href="src/index.html">src</a></td>
- <td data-value="78.94" class="pic medium">
- <div class="chart">
- <div class="cover-fill" style="width: 78%"></div>
- <div class="cover-empty" style="width: 22%"></div>
- </div>
- </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="66.66" class="pct medium">66.66%</td>
- <td data-value="6" class="abs medium">4/6</td>
- <td data-value="77.77" class="pct medium">77.77%</td>
- <td data-value="9" class="abs medium">7/9</td>
- <td data-value="78.94" class="pct medium">78.94%</td>
- <td data-value="19" class="abs medium">15/19</td>
- </tr>
-
- <tr>
- <td class="file high" data-value="src/components">
- <a href="src/components/index.html">src/components</a>
- </td>
- <td data-value="86.78" 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.78" class="pct high">86.78%</td>
- <td data-value="227" class="abs high">197/227</td>
- <td data-value="77.2" class="pct medium">77.2%</td>
- <td data-value="136" class="abs medium">105/136</td>
- <td data-value="87.5" class="pct high">87.5%</td>
- <td data-value="64" class="abs high">56/64</td>
- <td data-value="89.26" class="pct high">89.26%</td>
- <td data-value="205" class="abs high">183/205</td>
- </tr>
- </tbody>
- </table>
- </div>
- <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-13T21:49:58.924Z
</div>
- <script src="prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="sorter.js"></script>
- <script src="block-navigation.js"></script>
- </body>
+ <div class='status-line medium'></div>
+ <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+ <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+ <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+ <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+ <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+ <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+ <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+ <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+ <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+ <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+ <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+ <td class="file medium" data-value="src"><a href="src/index.html">src</a></td>
+ <td data-value="76.47" 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="76.47" class="pct medium">76.47%</td>
+ <td data-value="34" class="abs medium">26/34</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="70" class="pct medium">70%</td>
+ <td data-value="10" class="abs medium">7/10</td>
+ <td data-value="78.78" class="pct medium">78.78%</td>
+ <td data-value="33" class="abs medium">26/33</td>
+ </tr>
+
+<tr>
+ <td class="file medium" data-value="src/components"><a href="src/components/index.html">src/components</a></td>
+ <td data-value="79.57" class="pic medium">
+ <div class="chart"><div class="cover-fill" style="width: 79%"></div><div class="cover-empty" style="width: 21%"></div></div>
+ </td>
+ <td data-value="79.57" class="pct medium">79.57%</td>
+ <td data-value="284" class="abs medium">226/284</td>
+ <td data-value="70.17" class="pct medium">70.17%</td>
+ <td data-value="171" class="abs medium">120/171</td>
+ <td data-value="75.9" class="pct medium">75.9%</td>
+ <td data-value="83" class="abs medium">63/83</td>
+ <td data-value="81.78" class="pct high">81.78%</td>
+ <td data-value="258" class="abs high">211/258</td>
+ </tr>
+
+</tbody>
+</table>
+</div>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/prettify.css b/frontend/coverage/prettify.css
index 006492c..b317a7c 100644
--- a/frontend/coverage/prettify.css
+++ b/frontend/coverage/prettify.css
@@ -1,101 +1 @@
-.pln {
- color: #000;
-}
-@media screen {
- .str {
- color: #080;
- }
- .kwd {
- color: #008;
- }
- .com {
- color: #800;
- }
- .typ {
- color: #606;
- }
- .lit {
- color: #066;
- }
- .pun,
- .opn,
- .clo {
- color: #660;
- }
- .tag {
- color: #008;
- }
- .atn {
- color: #606;
- }
- .atv {
- color: #080;
- }
- .dec,
- .var {
- color: #606;
- }
- .fun {
- color: red;
- }
-}
-@media print, projection {
- .str {
- color: #060;
- }
- .kwd {
- color: #006;
- font-weight: bold;
- }
- .com {
- color: #600;
- font-style: italic;
- }
- .typ {
- color: #404;
- font-weight: bold;
- }
- .lit {
- color: #044;
- }
- .pun,
- .opn,
- .clo {
- color: #440;
- }
- .tag {
- color: #006;
- font-weight: bold;
- }
- .atn {
- color: #404;
- }
- .atv {
- color: #060;
- }
-}
-pre.prettyprint {
- padding: 2px;
- border: 1px solid #888;
-}
-ol.linenums {
- margin-top: 0;
- margin-bottom: 0;
-}
-li.L0,
-li.L1,
-li.L2,
-li.L3,
-li.L5,
-li.L6,
-li.L7,
-li.L8 {
- list-style-type: none;
-}
-li.L1,
-li.L3,
-li.L5,
-li.L7,
-li.L9 {
- background: #eee;
-}
+.pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
diff --git a/frontend/coverage/prettify.js b/frontend/coverage/prettify.js
index a318211..b322523 100644
--- a/frontend/coverage/prettify.js
+++ b/frontend/coverage/prettify.js
@@ -1,937 +1,2 @@
-
-window.PR_SHOULD_USE_CONTINUATION = true;
-(function () {
- var h = ['break,continue,do,else,for,if,return,while'];
- var u = [
- h,
- 'auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile',
- ];
- var p = [
- u,
- 'catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof',
- ];
- var l = [
- p,
- 'alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where',
- ];
- var x = [
- p,
- 'abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient',
- ];
- var R = [
- x,
- 'as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var',
- ];
- var r =
- 'all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes';
- var w = [p, 'debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN'];
- var s =
- 'caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END';
- var I = [
- h,
- 'and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None',
- ];
- var f = [
- h,
- 'alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END',
- ];
- var H = [h, 'case,done,elif,esac,eval,fi,function,in,local,set,then,until'];
- var A = [l, R, w, s + I, f, H];
- var e =
- /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;
- var C = 'str';
- var z = 'kwd';
- var j = 'com';
- var O = 'typ';
- var G = 'lit';
- var L = 'pun';
- var F = 'pln';
- var m = 'tag';
- var E = 'dec';
- var J = 'src';
- var P = 'atn';
- var n = 'atv';
- var N = 'nocode';
- var M =
- '(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
- function k(Z) {
- var ad = 0;
- var S = false;
- var ac = false;
- for (var V = 0, U = Z.length; V < U; ++V) {
- var ae = Z[V];
- if (ae.ignoreCase) {
- ac = true;
- } else {
- if (/[a-z]/i.test(ae.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
- S = true;
- ac = false;
- break;
- }
- }
- }
- var Y = { b: 8, t: 9, n: 10, v: 11, f: 12, r: 13 };
- function ab(ah) {
- var ag = ah.charCodeAt(0);
- if (ag !== 92) {
- return ag;
- }
- var af = ah.charAt(1);
- ag = Y[af];
- if (ag) {
- return ag;
- } else {
- if ('0' <= af && af <= '7') {
- return parseInt(ah.substring(1), 8);
- } else {
- if (af === 'u' || af === 'x') {
- return parseInt(ah.substring(2), 16);
- } else {
- return ah.charCodeAt(1);
- }
- }
- }
- }
- function T(af) {
- if (af < 32) {
- return (af < 16 ? '\\x0' : '\\x') + af.toString(16);
- }
- var ag = String.fromCharCode(af);
- if (ag === '\\' || ag === '-' || ag === '[' || ag === ']') {
- ag = '\\' + ag;
- }
- return ag;
- }
- function X(am) {
- var aq = am
- .substring(1, am.length - 1)
- .match(
- new RegExp(
- '\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]',
- 'g'
- )
- );
- var ak = [];
- var af = [];
- var ao = aq[0] === '^';
- for (var ar = ao ? 1 : 0, aj = aq.length; ar < aj; ++ar) {
- var ah = aq[ar];
- if (/\\[bdsw]/i.test(ah)) {
- ak.push(ah);
- } else {
- var ag = ab(ah);
- var al;
- if (ar + 2 < aj && '-' === aq[ar + 1]) {
- al = ab(aq[ar + 2]);
- ar += 2;
- } else {
- al = ag;
- }
- af.push([ag, al]);
- if (!(al < 65 || ag > 122)) {
- if (!(al < 65 || ag > 90)) {
- af.push([Math.max(65, ag) | 32, Math.min(al, 90) | 32]);
- }
- if (!(al < 97 || ag > 122)) {
- af.push([Math.max(97, ag) & ~32, Math.min(al, 122) & ~32]);
- }
- }
- }
- }
- af.sort(function (av, au) {
- return av[0] - au[0] || au[1] - av[1];
- });
- var ai = [];
- var ap = [NaN, NaN];
- for (var ar = 0; ar < af.length; ++ar) {
- var at = af[ar];
- if (at[0] <= ap[1] + 1) {
- ap[1] = Math.max(ap[1], at[1]);
- } else {
- ai.push((ap = at));
- }
- }
- var an = ['['];
- if (ao) {
- an.push('^');
- }
- an.push.apply(an, ak);
- for (var ar = 0; ar < ai.length; ++ar) {
- var at = ai[ar];
- an.push(T(at[0]));
- if (at[1] > at[0]) {
- if (at[1] + 1 > at[0]) {
- an.push('-');
- }
- an.push(T(at[1]));
- }
- }
- an.push(']');
- return an.join('');
- }
- function W(al) {
- var aj = al.source.match(
- new RegExp(
- '(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)',
- 'g'
- )
- );
- var ah = aj.length;
- var an = [];
- for (var ak = 0, am = 0; ak < ah; ++ak) {
- var ag = aj[ak];
- if (ag === '(') {
- ++am;
- } else {
- if ('\\' === ag.charAt(0)) {
- var af = +ag.substring(1);
- if (af && af <= am) {
- an[af] = -1;
- }
- }
- }
- }
- for (var ak = 1; ak < an.length; ++ak) {
- if (-1 === an[ak]) {
- an[ak] = ++ad;
- }
- }
- for (var ak = 0, am = 0; ak < ah; ++ak) {
- var ag = aj[ak];
- if (ag === '(') {
- ++am;
- if (an[am] === undefined) {
- aj[ak] = '(?:';
- }
- } else {
- if ('\\' === ag.charAt(0)) {
- var af = +ag.substring(1);
- if (af && af <= am) {
- aj[ak] = '\\' + an[am];
- }
- }
- }
- }
- for (var ak = 0, am = 0; ak < ah; ++ak) {
- if ('^' === aj[ak] && '^' !== aj[ak + 1]) {
- aj[ak] = '';
- }
- }
- if (al.ignoreCase && S) {
- for (var ak = 0; ak < ah; ++ak) {
- var ag = aj[ak];
- var ai = ag.charAt(0);
- if (ag.length >= 2 && ai === '[') {
- aj[ak] = X(ag);
- } else {
- if (ai !== '\\') {
- aj[ak] = ag.replace(/[a-zA-Z]/g, function (ao) {
- var ap = ao.charCodeAt(0);
- return '[' + String.fromCharCode(ap & ~32, ap | 32) + ']';
- });
- }
- }
- }
- }
- return aj.join('');
- }
- var aa = [];
- for (var V = 0, U = Z.length; V < U; ++V) {
- var ae = Z[V];
- if (ae.global || ae.multiline) {
- throw new Error('' + ae);
- }
- aa.push('(?:' + W(ae) + ')');
- }
- return new RegExp(aa.join('|'), ac ? 'gi' : 'g');
- }
- function a(V) {
- var U = /(?:^|\s)nocode(?:\s|$)/;
- var X = [];
- var T = 0;
- var Z = [];
- var W = 0;
- var S;
- if (V.currentStyle) {
- S = V.currentStyle.whiteSpace;
- } else {
- if (window.getComputedStyle) {
- S = document.defaultView.getComputedStyle(V, null).getPropertyValue('white-space');
- }
- }
- var Y = S && 'pre' === S.substring(0, 3);
- function aa(ab) {
- switch (ab.nodeType) {
- case 1:
- if (U.test(ab.className)) {
- return;
- }
- for (var ae = ab.firstChild; ae; ae = ae.nextSibling) {
- aa(ae);
- }
- var ad = ab.nodeName;
- if ('BR' === ad || 'LI' === ad) {
- X[W] = '\n';
- Z[W << 1] = T++;
- Z[(W++ << 1) | 1] = ab;
- }
- break;
- case 3:
- case 4:
- var ac = ab.nodeValue;
- if (ac.length) {
- if (!Y) {
- ac = ac.replace(/[ \t\r\n]+/g, ' ');
- } else {
- ac = ac.replace(/\r\n?/g, '\n');
- }
- X[W] = ac;
- Z[W << 1] = T;
- T += ac.length;
- Z[(W++ << 1) | 1] = ab;
- }
- break;
- }
- }
- aa(V);
- return { sourceCode: X.join('').replace(/\n$/, ''), spans: Z };
- }
- function B(S, U, W, T) {
- if (!U) {
- return;
- }
- var V = { sourceCode: U, basePos: S };
- W(V);
- T.push.apply(T, V.decorations);
- }
- var v = /\S/;
- function o(S) {
- var V = undefined;
- for (var U = S.firstChild; U; U = U.nextSibling) {
- var T = U.nodeType;
- V = T === 1 ? (V ? S : U) : T === 3 ? (v.test(U.nodeValue) ? S : V) : V;
- }
- return V === S ? undefined : V;
- }
- function g(U, T) {
- var S = {};
- var V;
- (function () {
- var ad = U.concat(T);
- var ah = [];
- var ag = {};
- for (var ab = 0, Z = ad.length; ab < Z; ++ab) {
- var Y = ad[ab];
- var ac = Y[3];
- if (ac) {
- for (var ae = ac.length; --ae >= 0; ) {
- S[ac.charAt(ae)] = Y;
- }
- }
- var af = Y[1];
- var aa = '' + af;
- if (!ag.hasOwnProperty(aa)) {
- ah.push(af);
- ag[aa] = null;
- }
- }
- ah.push(/[\0-\uffff]/);
- V = k(ah);
- })();
- var X = T.length;
- var W = function (ah) {
- var Z = ah.sourceCode,
- Y = ah.basePos;
- var ad = [Y, F];
- var af = 0;
- var an = Z.match(V) || [];
- var aj = {};
- for (var ae = 0, aq = an.length; ae < aq; ++ae) {
- var ag = an[ae];
- var ap = aj[ag];
- var ai = void 0;
- var am;
- if (typeof ap === 'string') {
- am = false;
- } else {
- var aa = S[ag.charAt(0)];
- if (aa) {
- ai = ag.match(aa[1]);
- ap = aa[0];
- } else {
- for (var ao = 0; ao < X; ++ao) {
- aa = T[ao];
- ai = ag.match(aa[1]);
- if (ai) {
- ap = aa[0];
- break;
- }
- }
- if (!ai) {
- ap = F;
- }
- }
- am = ap.length >= 5 && 'lang-' === ap.substring(0, 5);
- if (am && !(ai && typeof ai[1] === 'string')) {
- am = false;
- ap = J;
- }
- if (!am) {
- aj[ag] = ap;
- }
- }
- var ab = af;
- af += ag.length;
- if (!am) {
- ad.push(Y + ab, ap);
- } else {
- var al = ai[1];
- var ak = ag.indexOf(al);
- var ac = ak + al.length;
- if (ai[2]) {
- ac = ag.length - ai[2].length;
- ak = ac - al.length;
- }
- var ar = ap.substring(5);
- B(Y + ab, ag.substring(0, ak), W, ad);
- B(Y + ab + ak, al, q(ar, al), ad);
- B(Y + ab + ac, ag.substring(ac), W, ad);
- }
- }
- ah.decorations = ad;
- };
- return W;
- }
- function i(T) {
- var W = [],
- S = [];
- if (T.tripleQuotedStrings) {
- W.push([
- C,
- /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
- null,
- '\'"',
- ]);
- } else {
- if (T.multiLineStrings) {
- W.push([
- C,
- /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
- null,
- '\'"`',
- ]);
- } else {
- W.push([
- C,
- /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
- null,
- '"\'',
- ]);
- }
- }
- if (T.verbatimStrings) {
- S.push([C, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
- }
- var Y = T.hashComments;
- if (Y) {
- if (T.cStyleComments) {
- if (Y > 1) {
- W.push([j, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
- } else {
- W.push([
- j,
- /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,
- null,
- '#',
- ]);
- }
- S.push([C, /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/, null]);
- } else {
- W.push([j, /^#[^\r\n]*/, null, '#']);
- }
- }
- if (T.cStyleComments) {
- S.push([j, /^\/\/[^\r\n]*/, null]);
- S.push([j, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
- }
- if (T.regexLiterals) {
- var X =
- '/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/';
- S.push(['lang-regex', new RegExp('^' + M + '(' + X + ')')]);
- }
- var V = T.types;
- if (V) {
- S.push([O, V]);
- }
- var U = ('' + T.keywords).replace(/^ | $/g, '');
- if (U.length) {
- S.push([z, new RegExp('^(?:' + U.replace(/[\s,]+/g, '|') + ')\\b'), null]);
- }
- W.push([F, /^\s+/, null, ' \r\n\t\xA0']);
- S.push(
- [G, /^@[a-z_$][a-z_$@0-9]*/i, null],
- [O, /^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/, null],
- [F, /^[a-z_$][a-z_$@0-9]*/i, null],
- [
- G,
- new RegExp(
- '^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*',
- 'i'
- ),
- null,
- '0123456789',
- ],
- [F, /^\\[\s\S]?/, null],
- [L, /^.[^\s\w\.$@\'\"\`\/\#\\]*/, null]
- );
- return g(W, S);
- }
- var K = i({
- keywords: A,
- hashComments: true,
- cStyleComments: true,
- multiLineStrings: true,
- regexLiterals: true,
- });
- function Q(V, ag) {
- var U = /(?:^|\s)nocode(?:\s|$)/;
- var ab = /\r\n?|\n/;
- var ac = V.ownerDocument;
- var S;
- if (V.currentStyle) {
- S = V.currentStyle.whiteSpace;
- } else {
- if (window.getComputedStyle) {
- S = ac.defaultView.getComputedStyle(V, null).getPropertyValue('white-space');
- }
- }
- var Z = S && 'pre' === S.substring(0, 3);
- var af = ac.createElement('LI');
- while (V.firstChild) {
- af.appendChild(V.firstChild);
- }
- var W = [af];
- function ae(al) {
- switch (al.nodeType) {
- case 1:
- if (U.test(al.className)) {
- break;
- }
- if ('BR' === al.nodeName) {
- ad(al);
- if (al.parentNode) {
- al.parentNode.removeChild(al);
- }
- } else {
- for (var an = al.firstChild; an; an = an.nextSibling) {
- ae(an);
- }
- }
- break;
- case 3:
- case 4:
- if (Z) {
- var am = al.nodeValue;
- var aj = am.match(ab);
- if (aj) {
- var ai = am.substring(0, aj.index);
- al.nodeValue = ai;
- var ah = am.substring(aj.index + aj[0].length);
- if (ah) {
- var ak = al.parentNode;
- ak.insertBefore(ac.createTextNode(ah), al.nextSibling);
- }
- ad(al);
- if (!ai) {
- al.parentNode.removeChild(al);
- }
- }
- }
- break;
- }
- }
- function ad(ak) {
- while (!ak.nextSibling) {
- ak = ak.parentNode;
- if (!ak) {
- return;
- }
- }
- function ai(al, ar) {
- var aq = ar ? al.cloneNode(false) : al;
- var ao = al.parentNode;
- if (ao) {
- var ap = ai(ao, 1);
- var an = al.nextSibling;
- ap.appendChild(aq);
- for (var am = an; am; am = an) {
- an = am.nextSibling;
- ap.appendChild(am);
- }
- }
- return aq;
- }
- var ah = ai(ak.nextSibling, 0);
- for (var aj; (aj = ah.parentNode) && aj.nodeType === 1; ) {
- ah = aj;
- }
- W.push(ah);
- }
- for (var Y = 0; Y < W.length; ++Y) {
- ae(W[Y]);
- }
- if (ag === (ag | 0)) {
- W[0].setAttribute('value', ag);
- }
- var aa = ac.createElement('OL');
- aa.className = 'linenums';
- var X = Math.max(0, (ag - 1) | 0) || 0;
- for (var Y = 0, T = W.length; Y < T; ++Y) {
- af = W[Y];
- af.className = 'L' + ((Y + X) % 10);
- if (!af.firstChild) {
- af.appendChild(ac.createTextNode('\xA0'));
- }
- aa.appendChild(af);
- }
- V.appendChild(aa);
- }
- function D(ac) {
- var aj = /\bMSIE\b/.test(navigator.userAgent);
- var am = /\n/g;
- var al = ac.sourceCode;
- var an = al.length;
- var V = 0;
- var aa = ac.spans;
- var T = aa.length;
- var ah = 0;
- var X = ac.decorations;
- var Y = X.length;
- var Z = 0;
- X[Y] = an;
- var ar, aq;
- for (aq = ar = 0; aq < Y; ) {
- if (X[aq] !== X[aq + 2]) {
- X[ar++] = X[aq++];
- X[ar++] = X[aq++];
- } else {
- aq += 2;
- }
- }
- Y = ar;
- for (aq = ar = 0; aq < Y; ) {
- var at = X[aq];
- var ab = X[aq + 1];
- var W = aq + 2;
- while (W + 2 <= Y && X[W + 1] === ab) {
- W += 2;
- }
- X[ar++] = at;
- X[ar++] = ab;
- aq = W;
- }
- Y = X.length = ar;
- var ae = null;
- while (ah < T) {
- var af = aa[ah];
- var S = aa[ah + 2] || an;
- var ag = X[Z];
- var ap = X[Z + 2] || an;
- var W = Math.min(S, ap);
- var ak = aa[ah + 1];
- var U;
- if (ak.nodeType !== 1 && (U = al.substring(V, W))) {
- if (aj) {
- U = U.replace(am, '\r');
- }
- ak.nodeValue = U;
- var ai = ak.ownerDocument;
- var ao = ai.createElement('SPAN');
- ao.className = X[Z + 1];
- var ad = ak.parentNode;
- ad.replaceChild(ao, ak);
- ao.appendChild(ak);
- if (V < S) {
- aa[ah + 1] = ak = ai.createTextNode(al.substring(W, S));
- ad.insertBefore(ak, ao.nextSibling);
- }
- }
- V = W;
- if (V >= S) {
- ah += 2;
- }
- if (V >= ap) {
- Z += 2;
- }
- }
- }
- var t = {};
- function c(U, V) {
- for (var S = V.length; --S >= 0; ) {
- var T = V[S];
- if (!t.hasOwnProperty(T)) {
- t[T] = U;
- } else {
- if (window.console) {
- console.warn('cannot override language handler %s', T);
- }
- }
- }
- }
- function q(T, S) {
- if (!(T && t.hasOwnProperty(T))) {
- T = /^\s*</.test(S) ? 'default-markup' : 'default-code';
- }
- return t[T];
- }
- c(K, ['default-code']);
- c(
- g(
- [],
- [
- [F, /^[^<?]+/],
- [E, /^<!\w[^>]*(?:>|$)/],
- [j, /^<\!--[\s\S]*?(?:-\->|$)/],
- ['lang-', /^<\?([\s\S]+?)(?:\?>|$)/],
- ['lang-', /^<%([\s\S]+?)(?:%>|$)/],
- [L, /^(?:<[%?]|[%?]>)/],
- ['lang-', /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
- ['lang-js', /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
- ['lang-css', /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
- ['lang-in.tag', /^(<\/?[a-z][^<>]*>)/i],
- ]
- ),
- ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']
- );
- c(
- g(
- [
- [F, /^[\s]+/, null, ' \t\r\n'],
- [n, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '"\''],
- ],
- [
- [m, /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
- [P, /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
- ['lang-uq.val', /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
- [L, /^[=<>\/]+/],
- ['lang-js', /^on\w+\s*=\s*\"([^\"]+)\"/i],
- ['lang-js', /^on\w+\s*=\s*\'([^\']+)\'/i],
- ['lang-js', /^on\w+\s*=\s*([^\"\'>\s]+)/i],
- ['lang-css', /^style\s*=\s*\"([^\"]+)\"/i],
- ['lang-css', /^style\s*=\s*\'([^\']+)\'/i],
- ['lang-css', /^style\s*=\s*([^\"\'>\s]+)/i],
- ]
- ),
- ['in.tag']
- );
- c(g([], [[n, /^[\s\S]+/]]), ['uq.val']);
- c(i({ keywords: l, hashComments: true, cStyleComments: true, types: e }), [
- 'c',
- 'cc',
- 'cpp',
- 'cxx',
- 'cyc',
- 'm',
- ]);
- c(i({ keywords: 'null,true,false' }), ['json']);
- c(i({ keywords: R, hashComments: true, cStyleComments: true, verbatimStrings: true, types: e }), [
- 'cs',
- ]);
- c(i({ keywords: x, cStyleComments: true }), ['java']);
- c(i({ keywords: H, hashComments: true, multiLineStrings: true }), ['bsh', 'csh', 'sh']);
- c(i({ keywords: I, hashComments: true, multiLineStrings: true, tripleQuotedStrings: true }), [
- 'cv',
- 'py',
- ]);
- c(i({ keywords: s, hashComments: true, multiLineStrings: true, regexLiterals: true }), [
- 'perl',
- 'pl',
- 'pm',
- ]);
- c(i({ keywords: f, hashComments: true, multiLineStrings: true, regexLiterals: true }), ['rb']);
- c(i({ keywords: w, cStyleComments: true, regexLiterals: true }), ['js']);
- c(
- i({
- keywords: r,
- hashComments: 3,
- cStyleComments: true,
- multilineStrings: true,
- tripleQuotedStrings: true,
- regexLiterals: true,
- }),
- ['coffee']
- );
- c(g([], [[C, /^[\s\S]+/]]), ['regex']);
- function d(V) {
- var U = V.langExtension;
- try {
- var S = a(V.sourceNode);
- var T = S.sourceCode;
- V.sourceCode = T;
- V.spans = S.spans;
- V.basePos = 0;
- q(U, T)(V);
- D(V);
- } catch (W) {
- if ('console' in window) {
- console.log(W && W.stack ? W.stack : W);
- }
- }
- }
- function y(W, V, U) {
- var S = document.createElement('PRE');
- S.innerHTML = W;
- if (U) {
- Q(S, U);
- }
- var T = { langExtension: V, numberLines: U, sourceNode: S };
- d(T);
- return S.innerHTML;
- }
- function b(ad) {
- function Y(af) {
- return document.getElementsByTagName(af);
- }
- var ac = [Y('pre'), Y('code'), Y('xmp')];
- var T = [];
- for (var aa = 0; aa < ac.length; ++aa) {
- for (var Z = 0, V = ac[aa].length; Z < V; ++Z) {
- T.push(ac[aa][Z]);
- }
- }
- ac = null;
- var W = Date;
- if (!W.now) {
- W = {
- now: function () {
- return +new Date();
- },
- };
- }
- var X = 0;
- var S;
- var ab = /\blang(?:uage)?-([\w.]+)(?!\S)/;
- var ae = /\bprettyprint\b/;
- function U() {
- var ag = window.PR_SHOULD_USE_CONTINUATION ? W.now() + 250 : Infinity;
- for (; X < T.length && W.now() < ag; X++) {
- var aj = T[X];
- var ai = aj.className;
- if (ai.indexOf('prettyprint') >= 0) {
- var ah = ai.match(ab);
- var am;
- if (!ah && (am = o(aj)) && 'CODE' === am.tagName) {
- ah = am.className.match(ab);
- }
- if (ah) {
- ah = ah[1];
- }
- var al = false;
- for (var ak = aj.parentNode; ak; ak = ak.parentNode) {
- if (
- (ak.tagName === 'pre' || ak.tagName === 'code' || ak.tagName === 'xmp') &&
- ak.className &&
- ak.className.indexOf('prettyprint') >= 0
- ) {
- al = true;
- break;
- }
- }
- if (!al) {
- var af = aj.className.match(/\blinenums\b(?::(\d+))?/);
- af = af ? (af[1] && af[1].length ? +af[1] : true) : false;
- if (af) {
- Q(aj, af);
- }
- S = { langExtension: ah, sourceNode: aj, numberLines: af };
- d(S);
- }
- }
- }
- if (X < T.length) {
- setTimeout(U, 250);
- } else {
- if (ad) {
- ad();
- }
- }
- }
- U();
- }
- window.prettyPrintOne = y;
- window.prettyPrint = b;
- window.PR = {
- createSimpleLexer: g,
- registerLangHandler: c,
- sourceDecorator: i,
- PR_ATTRIB_NAME: P,
- PR_ATTRIB_VALUE: n,
- PR_COMMENT: j,
- PR_DECLARATION: E,
- PR_KEYWORD: z,
- PR_LITERAL: G,
- PR_NOCODE: N,
- PR_PLAIN: F,
- PR_PUNCTUATION: L,
- PR_SOURCE: J,
- PR_STRING: C,
- PR_TAG: m,
- PR_TYPE: O,
- };
-})();
-PR.registerLangHandler(
- PR.createSimpleLexer(
- [],
- [
- [PR.PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
- [PR.PR_COMMENT, /^<\!--[\s\S]*?(?:-\->|$)/],
- [PR.PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
- ['lang-', /^<\?([\s\S]+?)(?:\?>|$)/],
- ['lang-', /^<%([\s\S]+?)(?:%>|$)/],
- ['lang-', /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
- [
- 'lang-handlebars',
- /^<script\b[^>]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i,
- ],
- ['lang-js', /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
- ['lang-css', /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
- ['lang-in.tag', /^(<\/?[a-z][^<>]*>)/i],
- [PR.PR_DECLARATION, /^{{[#^>/]?\s*[\w.][^}]*}}/],
- [PR.PR_DECLARATION, /^{{&?\s*[\w.][^}]*}}/],
- [PR.PR_DECLARATION, /^{{{>?\s*[\w.][^}]*}}}/],
- [PR.PR_COMMENT, /^{{![^}]*}}/],
- ]
- ),
- ['handlebars', 'hbs']
-);
-PR.registerLangHandler(
- PR.createSimpleLexer(
- [[PR.PR_PLAIN, /^[ \t\r\n\f]+/, null, ' \t\r\n\f']],
- [
- [PR.PR_STRING, /^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/, null],
- [PR.PR_STRING, /^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/, null],
- ['lang-css-str', /^url\(([^\)\"\']*)\)/i],
- [
- PR.PR_KEYWORD,
- /^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,
- null,
- ],
- ['lang-css-kw', /^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],
- [PR.PR_COMMENT, /^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],
- [PR.PR_COMMENT, /^(?:<!--|-->)/],
- [PR.PR_LITERAL, /^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],
- [PR.PR_LITERAL, /^#(?:[0-9a-f]{3}){1,2}/i],
- [PR.PR_PLAIN, /^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],
- [PR.PR_PUNCTUATION, /^[^\s\w\'\"]+/],
- ]
- ),
- ['css']
-);
-PR.registerLangHandler(
- PR.createSimpleLexer(
- [],
- [[PR.PR_KEYWORD, /^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]
- ),
- ['css-kw']
-);
-PR.registerLangHandler(PR.createSimpleLexer([], [[PR.PR_STRING, /^[^\)\"\']+/]]), ['css-str']);
+/* eslint-disable */
+window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V<U;++V){var ae=Z[V];if(ae.ignoreCase){ac=true}else{if(/[a-z]/i.test(ae.source.replace(/\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi,""))){S=true;ac=false;break}}}var Y={b:8,t:9,n:10,v:11,f:12,r:13};function ab(ah){var ag=ah.charCodeAt(0);if(ag!==92){return ag}var af=ah.charAt(1);ag=Y[af];if(ag){return ag}else{if("0"<=af&&af<="7"){return parseInt(ah.substring(1),8)}else{if(af==="u"||af==="x"){return parseInt(ah.substring(2),16)}else{return ah.charCodeAt(1)}}}}function T(af){if(af<32){return(af<16?"\\x0":"\\x")+af.toString(16)}var ag=String.fromCharCode(af);if(ag==="\\"||ag==="-"||ag==="["||ag==="]"){ag="\\"+ag}return ag}function X(am){var aq=am.substring(1,am.length-1).match(new RegExp("\\\\u[0-9A-Fa-f]{4}|\\\\x[0-9A-Fa-f]{2}|\\\\[0-3][0-7]{0,2}|\\\\[0-7]{1,2}|\\\\[\\s\\S]|-|[^-\\\\]","g"));var ak=[];var af=[];var ao=aq[0]==="^";for(var ar=ao?1:0,aj=aq.length;ar<aj;++ar){var ah=aq[ar];if(/\\[bdsw]/i.test(ah)){ak.push(ah)}else{var ag=ab(ah);var al;if(ar+2<aj&&"-"===aq[ar+1]){al=ab(aq[ar+2]);ar+=2}else{al=ag}af.push([ag,al]);if(!(al<65||ag>122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;ar<af.length;++ar){var at=af[ar];if(at[0]<=ap[1]+1){ap[1]=Math.max(ap[1],at[1])}else{ai.push(ap=at)}}var an=["["];if(ao){an.push("^")}an.push.apply(an,ak);for(var ar=0;ar<ai.length;++ar){var at=ai[ar];an.push(T(at[0]));if(at[1]>at[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak<ah;++ak){var ag=aj[ak];if(ag==="("){++am}else{if("\\"===ag.charAt(0)){var af=+ag.substring(1);if(af&&af<=am){an[af]=-1}}}}for(var ak=1;ak<an.length;++ak){if(-1===an[ak]){an[ak]=++ad}}for(var ak=0,am=0;ak<ah;++ak){var ag=aj[ak];if(ag==="("){++am;if(an[am]===undefined){aj[ak]="(?:"}}else{if("\\"===ag.charAt(0)){var af=+ag.substring(1);if(af&&af<=am){aj[ak]="\\"+an[am]}}}}for(var ak=0,am=0;ak<ah;++ak){if("^"===aj[ak]&&"^"!==aj[ak+1]){aj[ak]=""}}if(al.ignoreCase&&S){for(var ak=0;ak<ah;++ak){var ag=aj[ak];var ai=ag.charAt(0);if(ag.length>=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V<U;++V){var ae=Z[V];if(ae.global||ae.multiline){throw new Error(""+ae)}aa.push("(?:"+W(ae)+")")}return new RegExp(aa.join("|"),ac?"gi":"g")}function a(V){var U=/(?:^|\s)nocode(?:\s|$)/;var X=[];var T=0;var Z=[];var W=0;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=document.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Y=S&&"pre"===S.substring(0,3);function aa(ab){switch(ab.nodeType){case 1:if(U.test(ab.className)){return}for(var ae=ab.firstChild;ae;ae=ae.nextSibling){aa(ae)}var ad=ab.nodeName;if("BR"===ad||"LI"===ad){X[W]="\n";Z[W<<1]=T++;Z[(W++<<1)|1]=ab}break;case 3:case 4:var ac=ab.nodeValue;if(ac.length){if(!Y){ac=ac.replace(/[ \t\r\n]+/g," ")}else{ac=ac.replace(/\r\n?/g,"\n")}X[W]=ac;Z[W<<1]=T;T+=ac.length;Z[(W++<<1)|1]=ab}break}}aa(V);return{sourceCode:X.join("").replace(/\n$/,""),spans:Z}}function B(S,U,W,T){if(!U){return}var V={sourceCode:U,basePos:S};W(V);T.push.apply(T,V.decorations)}var v=/\S/;function o(S){var V=undefined;for(var U=S.firstChild;U;U=U.nextSibling){var T=U.nodeType;V=(T===1)?(V?S:U):(T===3)?(v.test(U.nodeValue)?S:V):V}return V===S?undefined:V}function g(U,T){var S={};var V;(function(){var ad=U.concat(T);var ah=[];var ag={};for(var ab=0,Z=ad.length;ab<Z;++ab){var Y=ad[ab];var ac=Y[3];if(ac){for(var ae=ac.length;--ae>=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae<aq;++ae){var ag=an[ae];var ap=aj[ag];var ai=void 0;var am;if(typeof ap==="string"){am=false}else{var aa=S[ag.charAt(0)];if(aa){ai=ag.match(aa[1]);ap=aa[0]}else{for(var ao=0;ao<X;++ao){aa=T[ao];ai=ag.match(aa[1]);if(ai){ap=aa[0];break}}if(!ai){ap=F}}am=ap.length>=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y<W.length;++Y){ae(W[Y])}if(ag===(ag|0)){W[0].setAttribute("value",ag)}var aa=ac.createElement("OL");aa.className="linenums";var X=Math.max(0,((ag-1))|0)||0;for(var Y=0,T=W.length;Y<T;++Y){af=W[Y];af.className="L"+((Y+X)%10);if(!af.firstChild){af.appendChild(ac.createTextNode("\xA0"))}aa.appendChild(af)}V.appendChild(aa)}function D(ac){var aj=/\bMSIE\b/.test(navigator.userAgent);var am=/\n/g;var al=ac.sourceCode;var an=al.length;var V=0;var aa=ac.spans;var T=aa.length;var ah=0;var X=ac.decorations;var Y=X.length;var Z=0;X[Y]=an;var ar,aq;for(aq=ar=0;aq<Y;){if(X[aq]!==X[aq+2]){X[ar++]=X[aq++];X[ar++]=X[aq++]}else{aq+=2}}Y=ar;for(aq=ar=0;aq<Y;){var at=X[aq];var ab=X[aq+1];var W=aq+2;while(W+2<=Y&&X[W+1]===ab){W+=2}X[ar++]=at;X[ar++]=ab;aq=W}Y=X.length=ar;var ae=null;while(ah<T){var af=aa[ah];var S=aa[ah+2]||an;var ag=X[Z];var ap=X[Z+2]||an;var W=Math.min(S,ap);var ak=aa[ah+1];var U;if(ak.nodeType!==1&&(U=al.substring(V,W))){if(aj){U=U.replace(am,"\r")}ak.nodeValue=U;var ai=ak.ownerDocument;var ao=ai.createElement("SPAN");ao.className=X[Z+1];var ad=ak.parentNode;ad.replaceChild(ao,ak);ao.appendChild(ak);if(V<S){aa[ah+1]=ak=ai.createTextNode(al.substring(W,S));ad.insertBefore(ak,ao.nextSibling)}}V=W;if(V>=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*</.test(S)?"default-markup":"default-code"}return t[T]}c(K,["default-code"]);c(g([],[[F,/^[^<?]+/],[E,/^<!\w[^>]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa<ac.length;++aa){for(var Z=0,V=ac[aa].length;Z<V;++Z){T.push(ac[aa][Z])}}ac=null;var W=Date;if(!W.now){W={now:function(){return +(new Date)}}}var X=0;var S;var ab=/\blang(?:uage)?-([\w.]+)(?!\S)/;var ae=/\bprettyprint\b/;function U(){var ag=(window.PR_SHOULD_USE_CONTINUATION?W.now()+250:Infinity);for(;X<T.length&&W.now()<ag;X++){var aj=T[X];var ai=aj.className;if(ai.indexOf("prettyprint")>=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X<T.length){setTimeout(U,250)}else{if(ad){ad()}}}U()}window.prettyPrintOne=y;window.prettyPrint=b;window.PR={createSimpleLexer:g,registerLangHandler:c,sourceDecorator:i,PR_ATTRIB_NAME:P,PR_ATTRIB_VALUE:n,PR_COMMENT:j,PR_DECLARATION:E,PR_KEYWORD:z,PR_LITERAL:G,PR_NOCODE:N,PR_PLAIN:F,PR_PUNCTUATION:L,PR_SOURCE:J,PR_STRING:C,PR_TAG:m,PR_TYPE:O}})();PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_DECLARATION,/^<!\w[^>]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^<script\b[^>]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:<!--|-->)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]);
diff --git a/frontend/coverage/sorter.js b/frontend/coverage/sorter.js
index c3fbef4..4ed70ae 100644
--- a/frontend/coverage/sorter.js
+++ b/frontend/coverage/sorter.js
@@ -1,205 +1,210 @@
-
-var addSorting = (function () {
- 'use strict';
- var cols,
- currentSort = {
- index: 0,
- desc: false,
- };
+/* eslint-disable */
+var addSorting = (function() {
+ 'use strict';
+ var cols,
+ currentSort = {
+ index: 0,
+ desc: false
+ };
- // returns the summary table element
- function getTable() {
- return document.querySelector('.coverage-summary');
- }
- // returns the thead element of the summary table
- function getTableHeader() {
- return getTable().querySelector('thead tr');
- }
- // returns the tbody element of the summary table
- function getTableBody() {
- return getTable().querySelector('tbody');
- }
- // returns the th element for nth column
- function getNthColumn(n) {
- return getTableHeader().querySelectorAll('th')[n];
- }
-
- function onFilterInput() {
- const searchValue = document.getElementById('fileSearch').value;
- const rows = document.getElementsByTagName('tbody')[0].children;
-
- // Try to create a RegExp from the searchValue. If it fails (invalid regex),
- // it will be treated as a plain text search
- let searchRegex;
- try {
- searchRegex = new RegExp(searchValue, 'i'); // 'i' for case-insensitive
- } catch (error) {
- searchRegex = null;
+ // returns the summary table element
+ function getTable() {
+ return document.querySelector('.coverage-summary');
+ }
+ // returns the thead element of the summary table
+ function getTableHeader() {
+ return getTable().querySelector('thead tr');
+ }
+ // returns the tbody element of the summary table
+ function getTableBody() {
+ return getTable().querySelector('tbody');
+ }
+ // returns the th element for nth column
+ function getNthColumn(n) {
+ return getTableHeader().querySelectorAll('th')[n];
}
- for (let i = 0; i < rows.length; i++) {
- const row = rows[i];
- let isMatch = false;
-
- if (searchRegex) {
- // If a valid regex was created, use it for matching
- isMatch = searchRegex.test(row.textContent);
- } else {
- // Otherwise, fall back to the original plain text search
- isMatch = row.textContent.toLowerCase().includes(searchValue.toLowerCase());
- }
+ function onFilterInput() {
+ const searchValue = document.getElementById('fileSearch').value;
+ const rows = document.getElementsByTagName('tbody')[0].children;
+
+ // Try to create a RegExp from the searchValue. If it fails (invalid regex),
+ // it will be treated as a plain text search
+ let searchRegex;
+ try {
+ searchRegex = new RegExp(searchValue, 'i'); // 'i' for case-insensitive
+ } catch (error) {
+ searchRegex = null;
+ }
- row.style.display = isMatch ? '' : 'none';
- }
- }
-
- // loads the search box
- function addSearchBox() {
- var template = document.getElementById('filterTemplate');
- var templateClone = template.content.cloneNode(true);
- templateClone.getElementById('fileSearch').oninput = onFilterInput;
- template.parentElement.appendChild(templateClone);
- }
-
- // loads all columns
- function loadColumns() {
- var colNodes = getTableHeader().querySelectorAll('th'),
- colNode,
- cols = [],
- col,
- i;
-
- for (i = 0; i < colNodes.length; i += 1) {
- colNode = colNodes[i];
- col = {
- key: colNode.getAttribute('data-col'),
- sortable: !colNode.getAttribute('data-nosort'),
- type: colNode.getAttribute('data-type') || 'string',
- };
- cols.push(col);
- if (col.sortable) {
- col.defaultDescSort = col.type === 'number';
- colNode.innerHTML = colNode.innerHTML + '<span class="sorter"></span>';
- }
+ for (let i = 0; i < rows.length; i++) {
+ const row = rows[i];
+ let isMatch = false;
+
+ if (searchRegex) {
+ // If a valid regex was created, use it for matching
+ isMatch = searchRegex.test(row.textContent);
+ } else {
+ // Otherwise, fall back to the original plain text search
+ isMatch = row.textContent
+ .toLowerCase()
+ .includes(searchValue.toLowerCase());
+ }
+
+ row.style.display = isMatch ? '' : 'none';
+ }
}
- return cols;
- }
- // attaches a data attribute to every tr element with an object
- // of data values keyed by column name
- function loadRowData(tableRow) {
- var tableCols = tableRow.querySelectorAll('td'),
- colNode,
- col,
- data = {},
- i,
- val;
- for (i = 0; i < tableCols.length; i += 1) {
- colNode = tableCols[i];
- col = cols[i];
- val = colNode.getAttribute('data-value');
- if (col.type === 'number') {
- val = Number(val);
- }
- data[col.key] = val;
+
+ // loads the search box
+ function addSearchBox() {
+ var template = document.getElementById('filterTemplate');
+ var templateClone = template.content.cloneNode(true);
+ templateClone.getElementById('fileSearch').oninput = onFilterInput;
+ template.parentElement.appendChild(templateClone);
}
- return data;
- }
- // loads all row data
- function loadData() {
- var rows = getTableBody().querySelectorAll('tr'),
- i;
-
- for (i = 0; i < rows.length; i += 1) {
- rows[i].data = loadRowData(rows[i]);
+
+ // loads all columns
+ function loadColumns() {
+ var colNodes = getTableHeader().querySelectorAll('th'),
+ colNode,
+ cols = [],
+ col,
+ i;
+
+ for (i = 0; i < colNodes.length; i += 1) {
+ colNode = colNodes[i];
+ col = {
+ key: colNode.getAttribute('data-col'),
+ sortable: !colNode.getAttribute('data-nosort'),
+ type: colNode.getAttribute('data-type') || 'string'
+ };
+ cols.push(col);
+ if (col.sortable) {
+ col.defaultDescSort = col.type === 'number';
+ colNode.innerHTML =
+ colNode.innerHTML + '<span class="sorter"></span>';
+ }
+ }
+ return cols;
}
- }
- // sorts the table using the data for the ith column
- function sortByIndex(index, desc) {
- var key = cols[index].key,
- sorter = function (a, b) {
- a = a.data[key];
- b = b.data[key];
- return a < b ? -1 : a > b ? 1 : 0;
- },
- finalSorter = sorter,
- tableBody = document.querySelector('.coverage-summary tbody'),
- rowNodes = tableBody.querySelectorAll('tr'),
- rows = [],
- i;
-
- if (desc) {
- finalSorter = function (a, b) {
- return -1 * sorter(a, b);
- };
+ // attaches a data attribute to every tr element with an object
+ // of data values keyed by column name
+ function loadRowData(tableRow) {
+ var tableCols = tableRow.querySelectorAll('td'),
+ colNode,
+ col,
+ data = {},
+ i,
+ val;
+ for (i = 0; i < tableCols.length; i += 1) {
+ colNode = tableCols[i];
+ col = cols[i];
+ val = colNode.getAttribute('data-value');
+ if (col.type === 'number') {
+ val = Number(val);
+ }
+ data[col.key] = val;
+ }
+ return data;
}
+ // loads all row data
+ function loadData() {
+ var rows = getTableBody().querySelectorAll('tr'),
+ i;
- for (i = 0; i < rowNodes.length; i += 1) {
- rows.push(rowNodes[i]);
- tableBody.removeChild(rowNodes[i]);
+ for (i = 0; i < rows.length; i += 1) {
+ rows[i].data = loadRowData(rows[i]);
+ }
}
+ // sorts the table using the data for the ith column
+ function sortByIndex(index, desc) {
+ var key = cols[index].key,
+ sorter = function(a, b) {
+ a = a.data[key];
+ b = b.data[key];
+ return a < b ? -1 : a > b ? 1 : 0;
+ },
+ finalSorter = sorter,
+ tableBody = document.querySelector('.coverage-summary tbody'),
+ rowNodes = tableBody.querySelectorAll('tr'),
+ rows = [],
+ i;
+
+ if (desc) {
+ finalSorter = function(a, b) {
+ return -1 * sorter(a, b);
+ };
+ }
+
+ for (i = 0; i < rowNodes.length; i += 1) {
+ rows.push(rowNodes[i]);
+ tableBody.removeChild(rowNodes[i]);
+ }
- rows.sort(finalSorter);
+ rows.sort(finalSorter);
- for (i = 0; i < rows.length; i += 1) {
- tableBody.appendChild(rows[i]);
- }
- }
- // removes sort indicators for current column being sorted
- function removeSortIndicators() {
- var col = getNthColumn(currentSort.index),
- cls = col.className;
-
- cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
- col.className = cls;
- }
- // adds sort indicators for current column being sorted
- function addSortIndicators() {
- getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted';
- }
- // adds event listeners for all sorter widgets
- function enableUI() {
- var i,
- el,
- ithSorter = function ithSorter(i) {
- var col = cols[i];
-
- return function () {
- var desc = col.defaultDescSort;
-
- if (currentSort.index === i) {
- desc = !currentSort.desc;
- }
- sortByIndex(i, desc);
- removeSortIndicators();
- currentSort.index = i;
- currentSort.desc = desc;
- addSortIndicators();
- };
- };
- for (i = 0; i < cols.length; i += 1) {
- if (cols[i].sortable) {
- // add the click event handler on the th so users
- // dont have to click on those tiny arrows
- el = getNthColumn(i).querySelector('.sorter').parentElement;
- if (el.addEventListener) {
- el.addEventListener('click', ithSorter(i));
- } else {
- el.attachEvent('onclick', ithSorter(i));
+ for (i = 0; i < rows.length; i += 1) {
+ tableBody.appendChild(rows[i]);
}
- }
}
- }
- // adds sorting functionality to the UI
- return function () {
- if (!getTable()) {
- return;
+ // removes sort indicators for current column being sorted
+ function removeSortIndicators() {
+ var col = getNthColumn(currentSort.index),
+ cls = col.className;
+
+ cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
+ col.className = cls;
+ }
+ // adds sort indicators for current column being sorted
+ function addSortIndicators() {
+ getNthColumn(currentSort.index).className += currentSort.desc
+ ? ' sorted-desc'
+ : ' sorted';
+ }
+ // adds event listeners for all sorter widgets
+ function enableUI() {
+ var i,
+ el,
+ ithSorter = function ithSorter(i) {
+ var col = cols[i];
+
+ return function() {
+ var desc = col.defaultDescSort;
+
+ if (currentSort.index === i) {
+ desc = !currentSort.desc;
+ }
+ sortByIndex(i, desc);
+ removeSortIndicators();
+ currentSort.index = i;
+ currentSort.desc = desc;
+ addSortIndicators();
+ };
+ };
+ for (i = 0; i < cols.length; i += 1) {
+ if (cols[i].sortable) {
+ // add the click event handler on the th so users
+ // dont have to click on those tiny arrows
+ el = getNthColumn(i).querySelector('.sorter').parentElement;
+ if (el.addEventListener) {
+ el.addEventListener('click', ithSorter(i));
+ } else {
+ el.attachEvent('onclick', ithSorter(i));
+ }
+ }
+ }
}
- cols = loadColumns();
- loadData();
- addSearchBox();
- addSortIndicators();
- enableUI();
- };
+ // adds sorting functionality to the UI
+ return function() {
+ if (!getTable()) {
+ return;
+ }
+ cols = loadColumns();
+ loadData();
+ addSearchBox();
+ addSortIndicators();
+ enableUI();
+ };
})();
window.addEventListener('load', addSorting);
diff --git a/frontend/coverage/src/App.css.html b/frontend/coverage/src/App.css.html
index a5aa753..5fca2a7 100644
--- a/frontend/coverage/src/App.css.html
+++ b/frontend/coverage/src/App.css.html
@@ -1,61 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/App.css</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 type='text/css'>
+ .coverage-summary .sorter {
+ background-image: url(../sort-arrow-sprite.png);
+ }
</style>
- </head>
-
- <body>
- <div class="wrapper">
- <div class="pad1">
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
<h1><a href="../index.html">All files</a> / <a href="index.html">src</a> App.css</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 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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line low"></div>
- <pre><table class="coverage">
+ </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>
@@ -128,69 +135,7 @@
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
+<a name='L73'></a><a href='#L73'>73</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -269,8 +214,6 @@
&nbsp;
body {
margin: 0;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
}
&nbsp;
/* Dashboard Layout */
@@ -282,60 +225,28 @@ body {
/* Prevent body scroll */
}
&nbsp;
-.dashboard-header {
- /* Legacy didn't really have a top header, but we need one for settings/logout.
- Keeping it minimal/flat or matching sidebar bg if we want to blend in.
- For now, let's make it dark gray to stand out less or match legacy dark mode if applicable.
- Actually, let's keep it distinct but apply the font styles. */
- background: #222;
- color: white;
- padding: 0.5rem 1rem;
- display: flex;
- justify-content: space-between;
- align-items: center;
- font-weight: bold;
-}
-&nbsp;
-.dashboard-header h1 {
- margin: 0;
- font-size: 1.2rem;
- font-variant: small-caps;
- text-transform: lowercase;
-}
-&nbsp;
-.nav-link,
-.logout-btn {
- font-weight: bold;
- font-variant: small-caps;
- text-transform: lowercase;
- font-size: 1rem;
- background: transparent;
- border: none;
- color: #ccc;
- cursor: pointer;
- margin-left: 1rem;
-}
-&nbsp;
-.nav-link:hover,
-.logout-btn:hover {
- color: white;
- text-decoration: underline;
-}
+/* Header styles removed as we moved to sidebar navigation */
&nbsp;
.dashboard-content {
display: flex;
flex: 1;
overflow: hidden;
+ position: relative;
}
&nbsp;
.dashboard-sidebar {
- width: 15rem;
+ width: 11rem;
background: var(--sidebar-bg);
- border-right: 1px solid #999;
+ border-right: 1px solid var(--border-color);
display: flex;
flex-direction: column;
overflow-y: auto;
- padding: 1rem;
+ transition: margin-left 0.4s ease;
+ /* No padding here, handled in FeedList */
+}
+&nbsp;
+.dashboard-sidebar.hidden {
+ margin-left: -11rem;
}
&nbsp;
.dashboard-main {
@@ -347,42 +258,44 @@ body {
}
&nbsp;
.dashboard-main&gt;* {
- max-width: 600px;
- margin: 0;
+ max-width: 35em;
+ margin: 0 auto;
}
&nbsp;
-.logout-btn {
+.fixed-toggle {
+ position: absolute;
+ top: 1rem;
+ left: 1rem;
+ z-index: 1000;
background: transparent;
- border: 1px solid rgba(255, 255, 255, 0.3);
- color: white;
- padding: 0.5rem 1rem;
- border-radius: 4px;
+ border: none;
+ font-size: 2rem;
+ line-height: 1;
cursor: pointer;
- transition: all 0.2s;
- font-size: 0.9rem;
+ padding: 0;
+ color: var(--text-color);
+ /* Inherit didn't work well if parent is transparent */
}
&nbsp;
-.logout-btn:hover {
- background: rgba(255, 255, 255, 0.1);
- border-color: rgba(255, 255, 255, 0.5);
+.fixed-toggle:hover {
+ transform: scale(1.1);
}</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-13T21:49:58.924Z
- </div>
- <script src="../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../sorter.js"></script>
- <script src="../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/App.tsx.html b/frontend/coverage/src/App.tsx.html
index ed02366..cd305cc 100644
--- a/frontend/coverage/src/App.tsx.html
+++ b/frontend/coverage/src/App.tsx.html
@@ -1,61 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/App.tsx</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="../prettify.css" />
<link rel="stylesheet" href="../base.css" />
<link rel="shortcut icon" type="image/x-icon" href="../favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
- <style type="text/css">
- .coverage-summary .sorter {
- background-image: url(../sort-arrow-sprite.png);
- }
+ <style type='text/css'>
+ .coverage-summary .sorter {
+ background-image: url(../sort-arrow-sprite.png);
+ }
</style>
- </head>
-
- <body>
- <div class="wrapper">
- <div class="pad1">
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
<h1><a href="../index.html">All files</a> / <a href="index.html">src</a> App.tsx</h1>
- <div class="clearfix">
- <div class="fl pad1y space-right2">
- <span class="strong">78.94% </span>
- <span class="quiet">Statements</span>
- <span class="fraction">15/19</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">66.66% </span>
- <span class="quiet">Branches</span>
- <span class="fraction">4/6</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">77.77% </span>
- <span class="quiet">Functions</span>
- <span class="fraction">7/9</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">78.94% </span>
- <span class="quiet">Lines</span>
- <span class="fraction">15/19</span>
- </div>
+ <div class='clearfix'>
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">72.72% </span>
+ <span class="quiet">Statements</span>
+ <span class='fraction'>16/22</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">62.5% </span>
+ <span class="quiet">Branches</span>
+ <span class='fraction'>10/16</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">62.5% </span>
+ <span class="quiet">Functions</span>
+ <span class='fraction'>5/8</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">72.72% </span>
+ <span class="quiet">Lines</span>
+ <span class='fraction'>16/22</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line medium"></div>
- <pre><table class="coverage">
+ </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>
<a name='L3'></a><a href='#L3'>3</a>
@@ -146,7 +153,17 @@
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -185,16 +202,18 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">1x</span>
-<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -215,8 +234,14 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -237,9 +262,10 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import React, { useEffect, useState } from 'react';
-import { BrowserRouter, Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom';
+import { BrowserRouter, Routes, Route, Navigate, useLocation } from 'react-router-dom';
import Login from './components/Login';
import './App.css';
+import { apiFetch } from './utils';
&nbsp;
// Protected Route wrapper
function RequireAuth({ children }: { children: React.ReactElement }) {
@@ -247,7 +273,7 @@ function RequireAuth({ children }: { children: React.ReactElement }) {
const location = useLocation();
&nbsp;
useEffect(() =&gt; {
- fetch('/api/auth')
+ apiFetch('/api/auth')
.then((res) =&gt; {
if (res.ok) {
setAuth(true);
@@ -273,26 +299,25 @@ import FeedList from './components/FeedList';
import FeedItems from './components/FeedItems';
import Settings from './components/Settings';
&nbsp;
-function Dashboard() {
- const navigate = useNavigate();
- return (
- &lt;div className="dashboard"&gt;
- &lt;header className="dashboard-header"&gt;
- &lt;h1&gt;Neko Reader&lt;/h1&gt;
- &lt;nav&gt;
- &lt;button onClick={<span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >n</span>avigate('/settings')} c</span>lassName="nav-link" style={{ color: 'white', marginRight: '1rem', background: 'none', border: 'none', cursor: 'pointer', fontSize: 'inherit', fontFamily: 'inherit' }}&gt;Settings&lt;/button&gt;
+function Dashboard({ theme, setTheme }: { theme: string; setTheme: (t: string) =&gt; void }) {
+ const [sidebarVisible, setSidebarVisible] = useState(true);
&nbsp;
- &lt;button onClick={() =&gt; {
- fetch('/api/logout', { method: 'POST' })
- .then(() =&gt; window.location.href = '/v2/login');
- }} className="logout-btn"&gt;
- Logout
- &lt;/button&gt;
- &lt;/nav&gt;
- &lt;/header&gt;
+ return (
+ &lt;div
+ className={`dashboard ${sidebarVisible ? 'sidebar-visible' : <span class="branch-1 cbranch-no" title="branch not covered" >'sidebar-hidden'}</span> theme-${theme}`}
+ &gt;
&lt;div className="dashboard-content"&gt;
- &lt;aside className="dashboard-sidebar"&gt;
- &lt;FeedList /&gt;
+ {!sidebarVisible &amp;&amp; (
+<span class="branch-1 cbranch-no" title="branch not covered" > &lt;button</span>
+ className="sidebar-toggle fixed-toggle"
+ onClick={<span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >s</span>etSidebarVisible(true)}</span>
+ title="Show Sidebar"
+ &gt;
+ 🐱
+ &lt;/button&gt;
+ )}
+ &lt;aside className={`dashboard-sidebar ${sidebarVisible ? '' : <span class="branch-1 cbranch-no" title="branch not covered" >'hidden'}</span>`}&gt;
+ &lt;FeedList theme={theme} setTheme={setTheme} setSidebarVisible={setSidebarVisible} /&gt;
&lt;/aside&gt;
&lt;main className="dashboard-main"&gt;
&lt;Routes&gt;
@@ -308,15 +333,24 @@ function Dashboard() {
}
&nbsp;
function App() {
+ const [theme, setTheme] = useState(localStorage.getItem('neko-theme') || 'light');
+&nbsp;
+ const handleSetTheme = <span class="fstat-no" title="function not covered" >(n</span>ewTheme: string) =&gt; {
+<span class="cstat-no" title="statement not covered" > setTheme(newTheme);</span>
+<span class="cstat-no" title="statement not covered" > localStorage.setItem('neko-theme', newTheme);</span>
+ };
+&nbsp;
+ const basename = window.location.pathname.startsWith('/v2') ? '/v2' : <span class="branch-1 cbranch-no" title="branch not covered" >'/';</span>
+&nbsp;
return (
- &lt;BrowserRouter basename="/v2"&gt;
+ &lt;BrowserRouter basename={basename}&gt;
&lt;Routes&gt;
&lt;Route path="/login" element={&lt;Login /&gt;} /&gt;
&lt;Route
path="/*"
element={
&lt;RequireAuth&gt;
- &lt;Dashboard /&gt;
+ &lt;Dashboard theme={theme} setTheme={handleSetTheme} /&gt;
&lt;/RequireAuth&gt;
}
/&gt;
@@ -328,22 +362,21 @@ function App() {
export default App;
&nbsp;</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-13T21:49:58.924Z
- </div>
- <script src="../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../sorter.js"></script>
- <script src="../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/components/FeedItem.css.html b/frontend/coverage/src/components/FeedItem.css.html
index f6fe1a3..213077f 100644
--- a/frontend/coverage/src/components/FeedItem.css.html
+++ b/frontend/coverage/src/components/FeedItem.css.html
@@ -1,64 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/components/FeedItem.css</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 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</a> FeedItem.css
- </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>
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
+ <h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> FeedItem.css</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line low"></div>
- <pre><table class="coverage">
+ </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>
@@ -175,7 +179,25 @@
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -292,139 +314,147 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">.feed-item {
- padding: 1rem;
- margin-top: 5rem;
- list-style: none;
- border-bottom: none;
+ padding: 1rem;
+ margin-top: 5rem;
+ list-style: none;
+ border-bottom: none;
}
&nbsp;
-.feed-item.read .item-title {
- color: #888;
- font-weight: normal;
-}
-&nbsp;
-.feed-item.unread .item-title {
- font-weight: bold;
-}
+/* removed read/unread specific font-weight to keep it always bold as requested */
&nbsp;
.item-header {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- margin-bottom: 0.5rem;
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 0.5rem;
}
&nbsp;
.item-title {
- font-size: 1.25rem;
- font-weight: bold;
- /* Legacy headers were bold */
- text-decoration: none;
- color: #333;
- display: block;
- flex: 1;
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-size: 1.8rem;
+ font-weight: bold;
+ text-decoration: none;
+ color: var(--link-color);
+ display: block;
+ flex: 1;
}
&nbsp;
.item-title:hover {
- text-decoration: none;
- color: blue;
- /* Legacy link color */
+ text-decoration: none;
+ color: var(--link-color);
}
&nbsp;
.item-actions {
- display: flex;
- gap: 0.5rem;
- margin-left: 1rem;
+ display: flex;
+ gap: 0.5rem;
+ margin-left: 1rem;
}
&nbsp;
/* Legacy controls were simple text/links, but buttons are fine if minimal */
.star-btn {
- background: none;
- border: none;
- cursor: pointer;
- font-size: 1.2rem;
- padding: 0 0.5rem 0 0;
- vertical-align: middle;
- transition: color 0.2s;
- line-height: 1;
+ background: none;
+ border: none;
+ cursor: pointer;
+ font-size: 1.25rem;
+ padding: 0 0 0 0.5rem;
+ vertical-align: middle;
+ transition: color 0.2s;
+ line-height: 1;
}
&nbsp;
.star-btn.is-starred {
- color: #ffd700;
- /* Gold */
+ color: blue;
}
&nbsp;
.star-btn.is-unstarred {
- color: #ccc;
+ color: var(--text-color);
+ opacity: 0.3;
}
&nbsp;
.star-btn:hover {
- color: #ffeb3b;
+ color: blue;
}
&nbsp;
.action-btn {
- background: whitesmoke;
- border: none;
- cursor: pointer;
- padding: 2px 6px;
- font-size: 1rem;
- color: blue;
- font-weight: bold;
+ background: var(--sidebar-bg);
+ border: 1px solid var(--border-color, #ccc);
+ cursor: pointer;
+ padding: 2px 6px;
+ font-size: 1rem;
+ color: blue;
+ font-weight: bold;
}
&nbsp;
.action-btn:hover {
- background-color: #eee;
+ background-color: #eee;
}
&nbsp;
.dateline {
- margin-top: 0;
- font-weight: normal;
- font-size: .75em;
- color: #ccc;
- margin-bottom: 1rem;
+ margin-top: 0;
+ font-weight: normal;
+ font-size: 0.75em;
+ color: #ccc;
+ margin-bottom: 1rem;
}
&nbsp;
.dateline a {
- color: #ccc;
- text-decoration: none;
+ color: #ccc;
+ text-decoration: none;
}
&nbsp;
.item-description {
- color: #000;
- line-height: 1.5;
- font-size: 1rem;
- margin-top: 1rem;
+ color: var(--text-color);
+ line-height: 1.5;
+ font-size: 1rem;
+ margin-top: 1rem;
}
&nbsp;
.item-description img {
- max-width: 100%;
- height: auto;
- display: block;
- margin: 1rem 0;
+ max-width: 100%;
+ height: auto;
+ display: block;
+ margin: 1rem 0;
}
&nbsp;
.item-description blockquote {
- padding: 1rem 1rem 0 1rem;
- border-left: 4px solid #ddd;
- color: #666;
- margin-left: 0;
+ padding: 1rem 1rem 0 1rem;
+ border-left: 4px solid var(--sidebar-bg);
+ color: var(--text-color);
+ opacity: 0.8;
+ margin-left: 0;
+}
+&nbsp;
+.scrape-btn {
+ background: var(--bg-color);
+ border: 1px solid var(--border-color, #ccc);
+ color: blue;
+ cursor: pointer;
+ font-family: 'Helvetica Neue';
+ font-weight: bold;
+ font-size: 0.8rem;
+ padding: 2px 6px;
+ margin-left: 0.5rem;
+}
+&nbsp;
+.scrape-btn:hover {
+ background: var(--sidebar-bg);
}</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-13T21:49:58.924Z
- </div>
- <script src="../../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../../sorter.js"></script>
- <script src="../../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/components/FeedItem.tsx.html b/frontend/coverage/src/components/FeedItem.tsx.html
index 5512b78..6e76131 100644
--- a/frontend/coverage/src/components/FeedItem.tsx.html
+++ b/frontend/coverage/src/components/FeedItem.tsx.html
@@ -1,64 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/components/FeedItem.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 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</a> FeedItem.tsx
- </h1>
- <div class="clearfix">
- <div class="fl pad1y space-right2">
- <span class="strong">78.94% </span>
- <span class="quiet">Statements</span>
- <span class="fraction">15/19</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">88.88% </span>
- <span class="quiet">Branches</span>
- <span class="fraction">16/18</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">85.71% </span>
- <span class="quiet">Functions</span>
- <span class="fraction">6/7</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">78.94% </span>
- <span class="quiet">Lines</span>
- <span class="fraction">15/19</span>
- </div>
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
+ <h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> FeedItem.tsx</h1>
+ <div class='clearfix'>
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">78.12% </span>
+ <span class="quiet">Statements</span>
+ <span class='fraction'>25/32</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">86.95% </span>
+ <span class="quiet">Branches</span>
+ <span class='fraction'>20/23</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">83.33% </span>
+ <span class="quiet">Functions</span>
+ <span class='fraction'>10/12</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">80.64% </span>
+ <span class="quiet">Lines</span>
+ <span class='fraction'>25/31</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line medium"></div>
- <pre><table class="coverage">
+ </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>
<a name='L3'></a><a href='#L3'>3</a>
@@ -143,7 +147,41 @@
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -151,16 +189,18 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">33x</span>
+<span class="cline-any cline-yes">33x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">21x</span>
-<span class="cline-any cline-yes">21x</span>
+<span class="cline-any cline-yes">33x</span>
+<span class="cline-any cline-yes">16x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">21x</span>
+<span class="cline-any cline-yes">33x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">21x</span>
+<span class="cline-any cline-yes">33x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">1x</span>
@@ -196,7 +236,28 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">21x</span>
+<span class="cline-any cline-yes">33x</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">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">33x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -227,108 +288,143 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { useState } from 'react';
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { useState, useEffect } from 'react';
import type { Item } from '../types';
import './FeedItem.css';
&nbsp;
+import { apiFetch } from '../utils';
+&nbsp;
interface FeedItemProps {
- item: Item;
+ item: Item;
}
&nbsp;
export default function FeedItem({ item: initialItem }: FeedItemProps) {
- const [item, setItem] = useState(initialItem);
- const [loading, setLoading] = useState(false);
+ const [item, setItem] = useState(initialItem);
+ const [loading, setLoading] = useState(false);
+&nbsp;
+ useEffect(() =&gt; {
+ setItem(initialItem);
+ }, [initialItem]);
&nbsp;
+ const toggleStar = () =&gt; {
+ updateItem({ ...item, starred: !item.starred });
+ };
&nbsp;
- const toggleStar = () =&gt; {
- updateItem({ ...item, starred: !item.starred });
- };
+ const updateItem = (newItem: Item) =&gt; {
+ setLoading(true);
+ // Optimistic update
+ const previousItem = item;
+ setItem(newItem);
&nbsp;
- const updateItem = (newItem: Item) =&gt; {
- setLoading(true);
- // Optimistic update
- const previousItem = item;
- setItem(newItem);
+ apiFetch(`/api/item/${newItem._id}`, {
+ method: 'PUT',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ _id: newItem._id,
+ read: newItem.read,
+ starred: newItem.starred,
+ }),
+ })
+ .then((res) =&gt; {
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) {
+<span class="cstat-no" title="statement not covered" > throw new Error('Failed to update item');</span>
+ }
+ return res.json();
+ })
+ .then(() =&gt; {
+ // Confirm with server response if needed, but for now we trust the optimistic update
+ // or we could setItem(updated) if the server returns the full object
+ setLoading(false);
+ })
+ .catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; {
+<span class="cstat-no" title="statement not covered" > console.error('Error updating item:', err);</span>
+ // Revert on error
+<span class="cstat-no" title="statement not covered" > setItem(previousItem);</span>
+<span class="cstat-no" title="statement not covered" > setLoading(false);</span>
+ });
+ };
&nbsp;
- fetch(`/api/item/${newItem._id}`, {
- method: 'PUT',
- headers: {
- 'Content-Type': 'application/json',
- },
- body: JSON.stringify({
- _id: newItem._id,
- read: newItem.read,
- starred: newItem.starred,
- }),
- })
- .then((res) =&gt; {
- <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) {
-<span class="cstat-no" title="statement not covered" > throw new Error('Failed to update item');</span>
- }
- return res.json();
- })
- .then(() =&gt; {
- // Confirm with server response if needed, but for now we trust the optimistic update
- // or we could setItem(updated) if the server returns the full object
- setLoading(false);
- })
- .catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; {
-<span class="cstat-no" title="statement not covered" > console.error('Error updating item:', err);</span>
- // Revert on error
-<span class="cstat-no" title="statement not covered" > setItem(previousItem);</span>
-<span class="cstat-no" title="statement not covered" > setLoading(false);</span>
- });
- };
+ const loadFullContent = (e: React.MouseEvent) =&gt; {
+ e.stopPropagation();
+ setLoading(true);
+ apiFetch(`/api/item/${item._id}`)
+ .then((res) =&gt; {
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to fetch full content');</span>
+ return res.json();
+ })
+ .then((data) =&gt; {
+ setItem({ ...item, ...data });
+ setLoading(false);
+ })
+ .catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; {
+<span class="cstat-no" title="statement not covered" > console.error('Error fetching full content:', err);</span>
+<span class="cstat-no" title="statement not covered" > setLoading(false);</span>
+ });
+ };
&nbsp;
- return (
- &lt;li className={`feed-item ${item.read ? 'read' : 'unread'} ${loading ? 'loading' : ''}`}&gt;
- &lt;div className="item-header"&gt;
- &lt;button
- onClick={(e) =&gt; {
- e.stopPropagation();
- toggleStar();
- }}
- className={`star-btn ${item.starred ? 'is-starred' : 'is-unstarred'}`}
- title={item.starred ? "Unstar" : "Star"}
- &gt;
- {item.starred ? '★' : '☆'}
- &lt;/button&gt;
- &lt;a href={item.url} target="_blank" rel="noopener noreferrer" className="item-title"&gt;
- {item.title || <span class="branch-1 cbranch-no" title="branch not covered" >'(No Title)'}</span>
- &lt;/a&gt;
- &lt;/div&gt;
- &lt;div className="dateline"&gt;
- &lt;a href={item.url} target="_blank" rel="noopener noreferrer"&gt;
- {new Date(item.publish_date).toLocaleDateString()}
- {item.feed_title &amp;&amp; ` - ${item.feed_title}`}
- &lt;/a&gt;
- &lt;div className="item-actions" style={{ display: 'inline-block', float: 'right' }}&gt;
- &lt;/div&gt;
- &lt;/div&gt;
- {item.description &amp;&amp; (
- &lt;div className="item-description" dangerouslySetInnerHTML={{ __html: item.description }} /&gt;
- )}
- &lt;/li&gt;
- );
+ return (
+ &lt;li className={`feed-item ${item.read ? 'read' : 'unread'} ${loading ? 'loading' : ''}`}&gt;
+ &lt;div className="item-header"&gt;
+ &lt;a href={item.url} target="_blank" rel="noopener noreferrer" className="item-title"&gt;
+ {item.title || <span class="branch-1 cbranch-no" title="branch not covered" >'(No Title)'}</span>
+ &lt;/a&gt;
+ &lt;button
+ onClick={(e) =&gt; {
+ e.stopPropagation();
+ toggleStar();
+ }}
+ className={`star-btn ${item.starred ? 'is-starred' : 'is-unstarred'}`}
+ title={item.starred ? 'Unstar' : 'Star'}
+ &gt;
+ ★
+ &lt;/button&gt;
+ &lt;/div&gt;
+ &lt;div className="dateline"&gt;
+ &lt;a href={item.url} target="_blank" rel="noopener noreferrer"&gt;
+ {new Date(item.publish_date).toLocaleDateString()}
+ {item.feed_title &amp;&amp; ` - ${item.feed_title}`}
+ &lt;/a&gt;
+ &lt;div className="item-actions" style={{ display: 'inline-block', float: 'right' }}&gt;
+ {!item.full_content &amp;&amp; (
+ &lt;button onClick={loadFullContent} className="scrape-btn" title="Load Full Content"&gt;
+ text
+ &lt;/button&gt;
+ )}
+ &lt;/div&gt;
+ &lt;/div&gt;
+ {(item.full_content || item.description) &amp;&amp; (
+ &lt;div
+ className="item-description"
+ dangerouslySetInnerHTML={{ __html: item.full_content || item.description }}
+ /&gt;
+ )}
+ &lt;/li&gt;
+ );
}
&nbsp;</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-13T21:49:58.924Z
- </div>
- <script src="../../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../../sorter.js"></script>
- <script src="../../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/components/FeedItems.css.html b/frontend/coverage/src/components/FeedItems.css.html
index 0b1c77d..7a971c6 100644
--- a/frontend/coverage/src/components/FeedItems.css.html
+++ b/frontend/coverage/src/components/FeedItems.css.html
@@ -1,64 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/components/FeedItems.css</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 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</a> FeedItems.css
- </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>
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
+ <h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> FeedItems.css</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line low"></div>
- <pre><table class="coverage">
+ </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>
@@ -81,23 +85,7 @@
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
+<a name='L23'></a><a href='#L23'>23</a></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -120,53 +108,44 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">.feed-items {
- padding: 1rem;
+ padding: 1rem 0;
+ /* Removing horizontal padding to avoid double-padding with FeedItem */
}
&nbsp;
.feed-items h2 {
- margin-top: 0;
- border-bottom: 2px solid #eee;
- padding-bottom: 0.5rem;
+ margin-top: 0;
+ border-bottom: 2px solid var(--border-color);
+ padding-bottom: 0.5rem;
}
&nbsp;
.item-list {
- list-style: none;
- padding: 0;
-}
-&nbsp;
-.selected-item-container {
- border-left: 4px solid #007bff;
- background-color: #f8f9fa;
- padding-left: 0.5rem;
- margin-left: -0.5rem;
- /* Compensate for padding/border to keep alignment */
- transition: background-color 0.2s;
+ list-style: none;
+ padding: 0;
}
&nbsp;
.loading-more {
- padding: 2rem;
- text-align: center;
- color: #888;
- font-size: 0.9rem;
- min-height: 50px;
+ padding: 2rem;
+ text-align: center;
+ color: #888;
+ font-size: 0.9rem;
+ min-height: 50px;
}</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-13T21:49:58.924Z
- </div>
- <script src="../../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../../sorter.js"></script>
- <script src="../../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/components/FeedItems.tsx.html b/frontend/coverage/src/components/FeedItems.tsx.html
index e57acf9..f6b7493 100644
--- a/frontend/coverage/src/components/FeedItems.tsx.html
+++ b/frontend/coverage/src/components/FeedItems.tsx.html
@@ -1,64 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/components/FeedItems.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 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</a> FeedItems.tsx
- </h1>
- <div class="clearfix">
- <div class="fl pad1y space-right2">
- <span class="strong">89.34% </span>
- <span class="quiet">Statements</span>
- <span class="fraction">109/122</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">77.21% </span>
- <span class="quiet">Branches</span>
- <span class="fraction">61/79</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">86.2% </span>
- <span class="quiet">Functions</span>
- <span class="fraction">25/29</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">89.09% </span>
- <span class="quiet">Lines</span>
- <span class="fraction">98/110</span>
- </div>
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
+ <h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> FeedItems.tsx</h1>
+ <div class='clearfix'>
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">88.97% </span>
+ <span class="quiet">Statements</span>
+ <span class='fraction'>113/127</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">75.3% </span>
+ <span class="quiet">Branches</span>
+ <span class='fraction'>61/81</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">86.2% </span>
+ <span class="quiet">Functions</span>
+ <span class='fraction'>25/29</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">88.69% </span>
+ <span class="quiet">Lines</span>
+ <span class='fraction'>102/115</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line high"></div>
- <pre><table class="coverage">
+ </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>
<a name='L3'></a><a href='#L3'>3</a>
@@ -282,7 +286,18 @@
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -298,6 +313,7 @@
<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">&nbsp;</span>
<span class="cline-any cline-yes">27x</span>
<span class="cline-any cline-yes">8x</span>
@@ -323,6 +339,11 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">8x</span>
+<span class="cline-any cline-yes">8x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">8x</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-yes">8x</span>
<span class="cline-any cline-no">&nbsp;</span>
@@ -330,6 +351,8 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">8x</span>
+<span class="cline-any cline-yes">8x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">8x</span>
@@ -363,77 +386,79 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">27x</span>
<span class="cline-any cline-yes">7x</span>
+<span class="cline-any cline-yes">7x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">27x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">27x</span>
-<span class="cline-any cline-yes">23x</span>
-<span class="cline-any cline-yes">3x</span>
-<span class="cline-any cline-neutral">&nbsp;</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-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">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">27x</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">1x</span>
-<span class="cline-any cline-no">&nbsp;</span>
-<span class="cline-any cline-no">&nbsp;</span>
-<span class="cline-any cline-no">&nbsp;</span>
-<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</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">&nbsp;</span>
+<span class="cline-any cline-yes">27x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">23x</span>
-<span class="cline-any cline-yes">23x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">27x</span>
+<span class="cline-any cline-yes">23x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</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">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">27x</span>
<span class="cline-any cline-yes">2x</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</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">&nbsp;</span>
-<span class="cline-any cline-yes">27x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">2x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">23x</span>
+<span class="cline-any cline-yes">23x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-no">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">27x</span>
@@ -477,7 +502,6 @@
<span class="cline-any cline-yes">27x</span>
<span class="cline-any cline-yes">14x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -510,242 +534,251 @@ import { useParams, useSearchParams } from 'react-router-dom';
import type { Item } from '../types';
import FeedItem from './FeedItem';
import './FeedItems.css';
+import { apiFetch } from '../utils';
&nbsp;
export default function FeedItems() {
- const { feedId, tagName } = useParams&lt;{ feedId: string; tagName: string }&gt;();
- const [searchParams] = useSearchParams();
- const filterFn = searchParams.get('filter') || 'unread';
+ const { feedId, tagName } = useParams&lt;{ feedId: string; tagName: string }&gt;();
+ const [searchParams] = useSearchParams();
+ const filterFn = searchParams.get('filter') || 'unread';
&nbsp;
- const [items, setItems] = useState&lt;Item[]&gt;([]);
- const [loading, setLoading] = useState(true);
- const [loadingMore, setLoadingMore] = useState(false);
- const [hasMore, setHasMore] = useState(true);
- const [error, setError] = useState('');
+ const [items, setItems] = useState&lt;Item[]&gt;([]);
+ const [loading, setLoading] = useState(true);
+ const [loadingMore, setLoadingMore] = useState(false);
+ const [hasMore, setHasMore] = useState(true);
+ const [error, setError] = useState('');
+ const [selectedIndex, setSelectedIndex] = useState(-1);
&nbsp;
- const fetchItems = (maxId?: string) =&gt; {
- if (maxId) {
- setLoadingMore(true);
- } else {
- setLoading(true);
- setItems([]);
- }
- setError('');
+ const fetchItems = (maxId?: string) =&gt; {
+ if (maxId) {
+ setLoadingMore(true);
+ } else {
+ setLoading(true);
+ setItems([]);
+ }
+ setError('');
&nbsp;
- let url = '/api/stream';
- const params = new URLSearchParams();
+ let url = '/api/stream';
+ const params = new URLSearchParams();
&nbsp;
- if (feedId) {
- params.append('feed_id', feedId);
- } else if (tagName) {
- params.append('tag', tagName);
- }
+ if (feedId) {
+ params.append('feed_id', feedId);
+ } else if (tagName) {
+ params.append('tag', tagName);
+ }
&nbsp;
- if (maxId) {
- params.append('max_id', maxId);
- }
+ if (maxId) {
+ params.append('max_id', maxId);
+ }
+&nbsp;
+ // Apply filters
+ const searchQuery = searchParams.get('q');
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (searchQuery) {
+<span class="cstat-no" title="statement not covered" > params.append('q', searchQuery);</span>
+ }
+&nbsp;
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (filterFn === 'all') {
+<span class="cstat-no" title="statement not covered" > params.append('read_filter', 'all');</span>
+ <span class="missing-if-branch" title="if path not taken" >I</span>} else if (filterFn === 'starred') {
+<span class="cstat-no" title="statement not covered" > params.append('starred', 'true');</span>
+<span class="cstat-no" title="statement not covered" > params.append('read_filter', 'all');</span>
+ } else {
+ // default to unread
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (!searchQuery) {
+ params.append('read_filter', 'unread');
+ }
+ }
+&nbsp;
+ const queryString = params.toString();
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (queryString) {
+ url += `?${queryString}`;
+ }
&nbsp;
- // Apply filters
- <span class="missing-if-branch" title="if path not taken" >I</span>if (filterFn === 'all') {
-<span class="cstat-no" title="statement not covered" > params.append('read_filter', 'all');</span>
- <span class="missing-if-branch" title="if path not taken" >I</span>} else if (filterFn === 'starred') {
-<span class="cstat-no" title="statement not covered" > params.append('starred', 'true');</span>
-<span class="cstat-no" title="statement not covered" > params.append('read_filter', 'all');</span>
+ apiFetch(url)
+ .then((res) =&gt; {
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) {
+<span class="cstat-no" title="statement not covered" > throw new Error('Failed to fetch items');</span>
+ }
+ return res.json();
+ })
+ .then((data) =&gt; {
+ if (maxId) {
+ setItems((prev) =&gt; [...prev, ...data]);
} else {
- // default to unread
- params.append('read_filter', 'unread');
+ setItems(data);
}
+ setHasMore(data.length &gt; 0);
+ setLoading(false);
+ setLoadingMore(false);
+ })
+ .catch((err) =&gt; {
+ setError(err.message);
+ setLoading(false);
+ setLoadingMore(false);
+ });
+ };
&nbsp;
- const queryString = params.toString();
- <span class="missing-if-branch" title="else path not taken" >E</span>if (queryString) {
- url += `?${queryString}`;
- }
+ useEffect(() =&gt; {
+ fetchItems();
+ setSelectedIndex(-1);
+ }, [feedId, tagName, filterFn, searchParams]);
&nbsp;
- fetch(url)
- .then((res) =&gt; {
- <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) {
-<span class="cstat-no" title="statement not covered" > throw new Error('Failed to fetch items');</span>
- }
- return res.json();
- })
- .then((data) =&gt; {
- if (maxId) {
- setItems((prev) =&gt; [...prev, ...data]);
- } else {
- setItems(data);
- }
- setHasMore(data.length &gt; 0);
- setLoading(false);
- setLoadingMore(false);
- })
- .catch((err) =&gt; {
- setError(err.message);
- setLoading(false);
- setLoadingMore(false);
- });
- };
&nbsp;
- useEffect(() =&gt; {
- fetchItems();
- }, [feedId, tagName, filterFn]);
+ const scrollToItem = (index: number) =&gt; {
+ const element = document.getElementById(`item-${index}`);
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (element) {
+ element.scrollIntoView({ behavior: 'auto', block: 'start' });
+ }
+ };
&nbsp;
- const [selectedIndex, setSelectedIndex] = useState(-1);
+ const markAsRead = (item: Item) =&gt; {
+ const updatedItem = { ...item, read: true };
+ // Optimistic update
+ setItems((prevItems) =&gt; prevItems.map((i) =&gt; (i._id === item._id ? updatedItem : i)));
&nbsp;
- useEffect(() =&gt; {
- const handleKeyDown = (e: KeyboardEvent) =&gt; {
- <span class="missing-if-branch" title="if path not taken" >I</span>if (items.length === 0) <span class="cstat-no" title="statement not covered" >return;</span>
+ apiFetch(`/api/item/${item._id}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ read: true, starred: item.starred }),
+ }).catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; <span class="cstat-no" title="statement not covered" >console.error('Failed to mark read', err))</span>;
+ };
&nbsp;
- if (e.key === 'j') {
- setSelectedIndex((prev) =&gt; {
- const nextIndex = Math.min(prev + 1, items.length - 1);
- <span class="missing-if-branch" title="else path not taken" >E</span>if (nextIndex !== prev) {
- const item = items[nextIndex];
- if (!item.read) {
- markAsRead(item);
- }
- scrollToItem(nextIndex);
- }
- return nextIndex;
- });
- <span class="missing-if-branch" title="if path not taken" >I</span>} else if (e.key === 'k') {
-<span class="cstat-no" title="statement not covered" > setSelectedIndex(<span class="fstat-no" title="function not covered" >(p</span>rev) =&gt; {</span>
- const nextIndex = <span class="cstat-no" title="statement not covered" >Math.max(prev - 1, 0);</span>
-<span class="cstat-no" title="statement not covered" > if (nextIndex !== prev) {</span>
-<span class="cstat-no" title="statement not covered" > scrollToItem(nextIndex);</span>
- }
-<span class="cstat-no" title="statement not covered" > return nextIndex;</span>
- });
- <span class="missing-if-branch" title="else path not taken" >E</span>} else if (e.key === 's') {
- setSelectedIndex((currentIndex) =&gt; {
- <span class="missing-if-branch" title="else path not taken" >E</span>if (currentIndex &gt;= 0 &amp;&amp; currentIndex &lt; items.length) {
- toggleStar(items[currentIndex]);
- }
- return currentIndex;
- });
- }
- };
+ const toggleStar = (item: Item) =&gt; {
+ const updatedItem = { ...item, starred: !item.starred };
+ // Optimistic update
+ setItems((prevItems) =&gt; prevItems.map((i) =&gt; (i._id === item._id ? updatedItem : i)));
&nbsp;
- window.addEventListener('keydown', handleKeyDown);
- return () =&gt; window.removeEventListener('keydown', handleKeyDown);
- }, [items]);
+ apiFetch(`/api/item/${item._id}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ read: item.read, starred: !item.starred }),
+ }).catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; <span class="cstat-no" title="statement not covered" >console.error('Failed to toggle star', err))</span>;
+ };
&nbsp;
- const scrollToItem = (index: number) =&gt; {
- const element = document.getElementById(`item-${index}`);
- <span class="missing-if-branch" title="else path not taken" >E</span>if (element) {
- element.scrollIntoView({ behavior: 'smooth', block: 'start' });
- }
- };
+ useEffect(() =&gt; {
+ const handleKeyDown = (e: KeyboardEvent) =&gt; {
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (items.length === 0) <span class="cstat-no" title="statement not covered" >return;</span>
&nbsp;
- const markAsRead = (item: Item) =&gt; {
- const updatedItem = { ...item, read: true };
- // Optimistic update
- setItems((prevItems) =&gt; prevItems.map((i) =&gt; (i._id === item._id ? updatedItem : i)));
-&nbsp;
- fetch(`/api/item/${item._id}`, {
- method: 'PUT',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ read: true, starred: item.starred }),
- }).catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; <span class="cstat-no" title="statement not covered" >console.error('Failed to mark read', err))</span>;
+ if (e.key === 'j') {
+ setSelectedIndex((prev) =&gt; {
+ const nextIndex = Math.min(prev + 1, items.length - 1);
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (nextIndex !== prev) {
+ const item = items[nextIndex];
+ if (!item.read) {
+ markAsRead(item);
+ }
+ scrollToItem(nextIndex);
+ }
+ return nextIndex;
+ });
+ <span class="missing-if-branch" title="if path not taken" >I</span>} else if (e.key === 'k') {
+<span class="cstat-no" title="statement not covered" > setSelectedIndex(<span class="fstat-no" title="function not covered" >(p</span>rev) =&gt; {</span>
+ const nextIndex = <span class="cstat-no" title="statement not covered" >Math.max(prev - 1, 0);</span>
+<span class="cstat-no" title="statement not covered" > if (nextIndex !== prev) {</span>
+<span class="cstat-no" title="statement not covered" > scrollToItem(nextIndex);</span>
+ }
+<span class="cstat-no" title="statement not covered" > return nextIndex;</span>
+ });
+ <span class="missing-if-branch" title="else path not taken" >E</span>} else if (e.key === 's') {
+ setSelectedIndex((currentIndex) =&gt; {
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (currentIndex &gt;= 0 &amp;&amp; currentIndex &lt; items.length) {
+ toggleStar(items[currentIndex]);
+ }
+ return currentIndex;
+ });
+ }
};
&nbsp;
- const toggleStar = (item: Item) =&gt; {
- const updatedItem = { ...item, starred: !item.starred };
- // Optimistic update
- setItems((prevItems) =&gt; prevItems.map((i) =&gt; (i._id === item._id ? updatedItem : i)));
+ window.addEventListener('keydown', handleKeyDown);
+ return () =&gt; window.removeEventListener('keydown', handleKeyDown);
+ }, [items]);
&nbsp;
- fetch(`/api/item/${item._id}`, {
- method: 'PUT',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ read: item.read, starred: !item.starred }),
- }).catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; <span class="cstat-no" title="statement not covered" >console.error('Failed to toggle star', err))</span>;
- };
&nbsp;
- useEffect(() =&gt; {
- const observer = new IntersectionObserver(
- (entries) =&gt; {
- entries.forEach((entry) =&gt; {
- // Infinity scroll sentinel
- if (entry.target.id === 'load-more-sentinel') {
- <span class="missing-if-branch" title="else path not taken" >E</span>if (entry.isIntersecting &amp;&amp; !loadingMore &amp;&amp; hasMore &amp;&amp; items.length &gt; 0) {
- fetchItems(String(items[items.length - 1]._id));
- }
- return;
- }
&nbsp;
- // If item is not intersecting and is above the viewport, it's been scrolled past
- <span class="missing-if-branch" title="else path not taken" >E</span>if (!entry.isIntersecting &amp;&amp; entry.boundingClientRect.top &lt; 0) {
- const index = Number(entry.target.getAttribute('data-index'));
- <span class="missing-if-branch" title="else path not taken" >E</span>if (!isNaN(index) &amp;&amp; index &gt;= 0 &amp;&amp; index &lt; items.length) {
- const item = items[index];
- <span class="missing-if-branch" title="else path not taken" >E</span>if (!item.read) {
- markAsRead(item);
- }
- }
- }
- });
- },
- { root: null, threshold: 0 }
- );
+ useEffect(() =&gt; {
+ const observer = new IntersectionObserver(
+ (entries) =&gt; {
+ entries.forEach((entry) =&gt; {
+ // Infinity scroll sentinel
+ if (entry.target.id === 'load-more-sentinel') {
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (entry.isIntersecting &amp;&amp; !loadingMore &amp;&amp; hasMore &amp;&amp; items.length &gt; 0) {
+ fetchItems(String(items[items.length - 1]._id));
+ }
+ return;
+ }
&nbsp;
- items.forEach((_, index) =&gt; {
- const el = document.getElementById(`item-${index}`);
- <span class="missing-if-branch" title="else path not taken" >E</span>if (el) observer.observe(el);
+ // If item is not intersecting and is above the viewport, it's been scrolled past
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (!entry.isIntersecting &amp;&amp; entry.boundingClientRect.top &lt; 0) {
+ const index = Number(entry.target.getAttribute('data-index'));
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (!isNaN(index) &amp;&amp; index &gt;= 0 &amp;&amp; index &lt; items.length) {
+ const item = items[index];
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (!item.read) {
+ markAsRead(item);
+ }
+ }
+ }
});
+ },
+ { root: null, threshold: 0 }
+ );
&nbsp;
- const sentinel = document.getElementById('load-more-sentinel');
- if (sentinel) observer.observe(sentinel);
+ items.forEach((_, index) =&gt; {
+ const el = document.getElementById(`item-${index}`);
+ <span class="missing-if-branch" title="else path not taken" >E</span>if (el) observer.observe(el);
+ });
&nbsp;
- return () =&gt; observer.disconnect();
- }, [items, loadingMore, hasMore]);
+ const sentinel = document.getElementById('load-more-sentinel');
+ if (sentinel) observer.observe(sentinel);
&nbsp;
- if (loading) return &lt;div className="feed-items-loading"&gt;Loading items...&lt;/div&gt;;
- if (error) return &lt;div className="feed-items-error"&gt;Error: {error}&lt;/div&gt;;
+ return () =&gt; observer.disconnect();
+ }, [items, loadingMore, hasMore]);
&nbsp;
+ if (loading) return &lt;div className="feed-items-loading"&gt;Loading items...&lt;/div&gt;;
+ if (error) return &lt;div className="feed-items-error"&gt;Error: {error}&lt;/div&gt;;
&nbsp;
- return (
- &lt;div className="feed-items"&gt;
- {items.length === 0 ? (
-<span class="branch-0 cbranch-no" title="branch not covered" > &lt;p&gt;No items found.&lt;/p&gt;</span>
- ) : (
- &lt;ul className="item-list"&gt;
- {items.map((item, index) =&gt; (
- &lt;div
- id={`item-${index}`}
- key={item._id}
- data-index={index}
- className={index === selectedIndex ? 'selected-item-container' : ''}
- onClick={<span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >s</span>etSelectedIndex(index)}</span>
- &gt;
- &lt;FeedItem item={item} /&gt;
- &lt;/div&gt;
- ))}
- {hasMore &amp;&amp; (
- &lt;div id="load-more-sentinel" className="loading-more"&gt;
- {loadingMore ? 'Loading more...' : ''}
- &lt;/div&gt;
- )}
- &lt;/ul&gt;
- )}
- &lt;/div&gt;
- );
+ return (
+ &lt;div className="feed-items"&gt;
+ {items.length === 0 ? (
+<span class="branch-0 cbranch-no" title="branch not covered" > &lt;p&gt;No items found.&lt;/p&gt;</span>
+ ) : (
+ &lt;ul className="item-list"&gt;
+ {items.map((item, index) =&gt; (
+ &lt;div
+ id={`item-${index}`}
+ key={item._id}
+ data-index={index}
+ data-selected={index === selectedIndex}
+ onClick={<span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >s</span>etSelectedIndex(index)}</span>
+ &gt;
+ &lt;FeedItem item={item} /&gt;
+ &lt;/div&gt;
+ ))}
+ {hasMore &amp;&amp; (
+ &lt;div id="load-more-sentinel" className="loading-more"&gt;
+ {loadingMore ? 'Loading more...' : ''}
+ &lt;/div&gt;
+ )}
+ &lt;/ul&gt;
+ )}
+ &lt;/div&gt;
+ );
}
&nbsp;</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-13T21:49:58.924Z
- </div>
- <script src="../../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../../sorter.js"></script>
- <script src="../../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/components/FeedList.css.html b/frontend/coverage/src/components/FeedList.css.html
index fe60b9d..2b93e18 100644
--- a/frontend/coverage/src/components/FeedList.css.html
+++ b/frontend/coverage/src/components/FeedList.css.html
@@ -1,64 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/components/FeedList.css</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 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</a> FeedList.css
- </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>
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
+ <h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> FeedList.css</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line low"></div>
- <pre><table class="coverage">
+ </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>
@@ -156,7 +160,159 @@
<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">&nbsp;</span>
+<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -254,120 +410,195 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">.feed-list {
- /* Removed card styling */
- padding: 0;
- background: transparent;
+ padding: 1rem;
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ color: #777;
+ /* specific v1 color */
+ font-size: 0.8rem;
}
&nbsp;
-.feed-list h2 {
- font-size: 1.2rem;
- margin-bottom: 0.5rem;
- border-bottom: 1px solid #999;
- padding-bottom: 0.25rem;
- text-transform: uppercase;
- letter-spacing: 1px;
+.feed-list h1.logo {
+ font-size: 2rem;
+ /* match v1 */
+ margin: 0 0 1rem 0;
+ line-height: 1;
+ cursor: pointer;
+ position: sticky;
+ top: 0;
+ background: var(--sidebar-bg);
+ z-index: 10;
+ padding-bottom: 0.5rem;
+ color: var(--text-color);
+ /* Usually dark/white depending on theme, v1 was white on blue? No, white on fixed header? No, v1 logo class says color: white. But sidebar is #ccc. */
+ /* In v1 logo was fixed top left (blue header bar?). In v2 sidebar is #ccc.
+ Let's use theme text color but maybe bolder? */
}
&nbsp;
-.feed-list-items,
-.tag-list-items,
-.filter-list {
- list-style: none;
- padding: 0;
- margin: 0;
+/* Override logo color if necessary for themes */
+.theme-light .feed-list h1.logo {
+ color: #333;
}
&nbsp;
-.sidebar-feed-item {
- padding: 0.25rem 0;
- border-bottom: none;
- /* Clean look */
- display: flex;
- justify-content: space-between;
- align-items: center;
+.theme-dark .feed-list h1.logo {
+ color: #eee;
}
&nbsp;
-.feed-title {
- color: #333;
- text-decoration: none;
- font-size: 0.9rem;
+.search-section {
+ margin-bottom: 1rem;
}
&nbsp;
-.feed-title:hover {
- text-decoration: underline;
- color: #000;
+.search-input {
+ width: 100%;
+ padding: 0.25rem;
+ border: 1px solid var(--border-color, #999);
+ background: var(--bg-color);
+ color: var(--text-color);
+ font-size: 0.8rem;
+ font-family: inherit;
+ border-radius: 0;
+ /* v1 didn't have rounded inputs usually */
}
&nbsp;
-.feed-category {
- display: none;
- /* Hide category in sidebar list to save space/match legacy simplicity if needed */
+.section-header {
+ font-size: 1rem;
+ /* v1 h4 size? */
+ font-weight: bold;
+ margin: 1rem 0 0.25rem 0;
+ cursor: pointer;
+ user-select: none;
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ color: #333;
+ /* Darker than list items */
+ text-transform: lowercase;
+ font-variant: small-caps;
+}
+&nbsp;
+.filter-list,
+.tag-list-items,
+.feed-list-items,
+.nav-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
}
&nbsp;
-.tag-section {
- margin-top: 2rem;
+.filter-list li,
+.nav-list li {
+ margin-bottom: 0.1rem;
}
&nbsp;
+.filter-list a,
+.nav-list a,
.tag-link,
-.filter-list li a {
- color: #333;
- text-decoration: none;
- font-size: 0.9rem;
- display: block;
- padding: 0.1rem 0;
+.feed-title,
+.logout-link {
+ text-decoration: none;
+ color: var(--link-color, blue);
+ font-size: 0.8rem;
+ /* Matches v1 .75em approx */
+ display: block;
+ cursor: pointer;
+ background: none;
+ border: none;
+ padding: 0;
+ font-family: inherit;
+ font-variant: small-caps;
+ text-transform: lowercase;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
&nbsp;
+.filter-list a:hover,
+.nav-list a:hover,
.tag-link:hover,
-.filter-list li a:hover {
- text-decoration: underline;
- background: transparent;
- /* No hover bg */
- color: #000;
+.feed-title:hover,
+.logout-link:hover {
+ text-decoration: underline;
+ color: var(--link-color, blue);
}
&nbsp;
-.filter-section {
- margin-bottom: 2rem;
+.filter-list a.active,
+.tag-link.active,
+.feed-title.active {
+ font-weight: bold;
+ color: #000;
+ /* Active state black */
}
&nbsp;
-.filter-list {
- display: block;
- /* Stack vertically */
+.tag-item,
+.sidebar-feed-item {
+ margin-bottom: 0;
}
&nbsp;
-.filter-list {
- display: block;
+.feed-category {
+ display: none;
}
&nbsp;
-.filter-list li a {
- text-decoration: none;
- color: #333;
- font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
- font-weight: bold;
- font-variant: small-caps;
- text-transform: lowercase;
- font-size: 1.1rem;
- display: block;
- margin-bottom: 0.5rem;
+.nav-section {
+ margin-top: 2rem;
+ border-top: 1px solid var(--border-color, #999);
+ padding-top: 0.5rem;
}
&nbsp;
-.filter-list li a:hover {
- color: blue;
- background-color: transparent;
+.logout-link {
+ text-align: left;
+ width: 100%;
+ color: #777;
+ /* Make logout less prominent */
+}
+&nbsp;
+.logout-link:hover {
+ color: var(--link-color, blue);
+}
+&nbsp;
+.theme-section {
+ margin-top: 1rem;
+}
+&nbsp;
+.theme-selector {
+ display: flex;
+ gap: 0.5rem;
+}
+&nbsp;
+.theme-selector button {
+ background: transparent;
+ border: 1px solid var(--border-color, #999);
+ cursor: pointer;
+ padding: 0.1rem 0.3rem;
+ font-size: 0.9rem;
+ border-radius: 0;
+}
+&nbsp;
+.theme-selector button.active {
+ background: var(--border-color, #999);
+ color: white;
+}
+&nbsp;
+/* Scrollbar styling for webkit */
+.dashboard-sidebar::-webkit-scrollbar {
+ width: 4px;
+}
+&nbsp;
+.dashboard-sidebar::-webkit-scrollbar-thumb {
+ background-color: var(--border-color, #999);
}</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-13T21:49:58.924Z
- </div>
- <script src="../../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../../sorter.js"></script>
- <script src="../../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/components/FeedList.tsx.html b/frontend/coverage/src/components/FeedList.tsx.html
index ba7d81f..acb2ede 100644
--- a/frontend/coverage/src/components/FeedList.tsx.html
+++ b/frontend/coverage/src/components/FeedList.tsx.html
@@ -1,64 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/components/FeedList.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 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</a> FeedList.tsx
- </h1>
- <div class="clearfix">
- <div class="fl pad1y space-right2">
- <span class="strong">91.66% </span>
- <span class="quiet">Statements</span>
- <span class="fraction">22/24</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">82.35% </span>
- <span class="quiet">Branches</span>
- <span class="fraction">14/17</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">100% </span>
- <span class="quiet">Functions</span>
- <span class="fraction">8/8</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">100% </span>
- <span class="quiet">Lines</span>
- <span class="fraction">20/20</span>
- </div>
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
+ <h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> FeedList.tsx</h1>
+ <div class='clearfix'>
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">79.54% </span>
+ <span class="quiet">Statements</span>
+ <span class='fraction'>35/44</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">64.86% </span>
+ <span class="quiet">Branches</span>
+ <span class='fraction'>24/37</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">64.7% </span>
+ <span class="quiet">Functions</span>
+ <span class='fraction'>11/17</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">82.05% </span>
+ <span class="quiet">Lines</span>
+ <span class='fraction'>32/39</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line high"></div>
- <pre><table class="coverage">
+ </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>
<a name='L3'></a><a href='#L3'>3</a>
@@ -139,18 +143,153 @@
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-neutral">&nbsp;</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-yes">11x</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">11x</span>
+<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">13x</span>
<span class="cline-any cline-yes">6x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4x</span>
@@ -172,8 +311,56 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">11x</span>
-<span class="cline-any cline-yes">5x</span>
+<span class="cline-any cline-yes">13x</span>
+<span class="cline-any cline-yes">7x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-yes">1x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">6x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">4x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -191,7 +378,20 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">2x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -207,7 +407,6 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
-<span class="cline-any cline-yes">3x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -216,107 +415,226 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import { useEffect, useState } from 'react';
-import { Link } from 'react-router-dom';
+import { Link, useNavigate, useSearchParams, useLocation, useParams } from 'react-router-dom';
import type { Feed, Category } from '../types';
import './FeedList.css';
+import { apiFetch } from '../utils';
+&nbsp;
+export default function FeedList({
+ theme,
+ setTheme,
+ setSidebarVisible,
+}: {
+ theme: string;
+ setTheme: (t: string) =&gt; void;
+ setSidebarVisible: (visible: boolean) =&gt; void;
+}) {
+ const [feeds, setFeeds] = useState&lt;Feed[]&gt;([]);
+ const [tags, setTags] = useState&lt;Category[]&gt;([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState('');
+ const [feedsExpanded, setFeedsExpanded] = useState(false);
+ const [searchQuery, setSearchQuery] = useState('');
+ const navigate = useNavigate();
+ const [searchParams] = useSearchParams();
+ const location = useLocation();
+ const { feedId, tagName } = useParams();
+&nbsp;
+ const currentFilter =
+ searchParams.get('filter') ||
+ (location.pathname === '/' &amp;&amp; !feedId &amp;&amp; !tagName ? 'unread' : <span class="branch-1 cbranch-no" title="branch not covered" >'');</span>
+&nbsp;
+ const handleSearch = <span class="fstat-no" title="function not covered" >(e</span>: React.FormEvent) =&gt; {
+<span class="cstat-no" title="statement not covered" > e.preventDefault();</span>
+<span class="cstat-no" title="statement not covered" > if (searchQuery.trim()) {</span>
+<span class="cstat-no" title="statement not covered" > navigate(`/?q=${encodeURIComponent(searchQuery.trim())}`);</span>
+ }
+ };
+&nbsp;
+ const toggleFeeds = () =&gt; {
+ setFeedsExpanded(!feedsExpanded);
+ };
+&nbsp;
+ useEffect(() =&gt; {
+ Promise.all([
+ apiFetch('/api/feed/').then((res) =&gt; {
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to fetch feeds');</span>
+ return res.json();
+ }),
+ apiFetch('/api/tag').then((res) =&gt; {
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to fetch tags');</span>
+ return res.json();
+ }),
+ ])
+ .then(([feedsData, tagsData]) =&gt; {
+ setFeeds(feedsData);
+ setTags(tagsData);
+ setLoading(false);
+ })
+ .catch((err) =&gt; {
+ setError(err.message);
+ setLoading(false);
+ });
+ }, []);
&nbsp;
-export default function FeedList() {
- const [feeds, setFeeds] = useState&lt;Feed[]&gt;([]);
- const [tags, setTags] = useState&lt;Category[]&gt;([]);
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState('');
+ if (loading) return &lt;div className="feed-list-loading"&gt;Loading feeds...&lt;/div&gt;;
+ if (error) return &lt;div className="feed-list-error"&gt;Error: {error}&lt;/div&gt;;
&nbsp;
- useEffect(() =&gt; {
- Promise.all([
- fetch('/api/feed/').then(res =&gt; {
- <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to fetch feeds');</span>
- return res.json();
- }),
- fetch('/api/tag').then(res =&gt; {
- <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to fetch tags');</span>
- return res.json();
- })
- ])
- .then(([feedsData, tagsData]) =&gt; {
- setFeeds(feedsData);
- setTags(tagsData);
- setLoading(false);
- })
- .catch((err) =&gt; {
- setError(err.message);
- setLoading(false);
- });
- }, []);
+ const handleLogout = () =&gt; {
+ apiFetch('/api/logout', { method: 'POST' }).then(() =&gt; (window.location.href = '/v2/login'));
+ };
&nbsp;
- if (loading) return &lt;div className="feed-list-loading"&gt;Loading feeds...&lt;/div&gt;;
- if (error) return &lt;div className="feed-list-error"&gt;Error: {error}&lt;/div&gt;;
+ return (
+ &lt;div className="feed-list"&gt;
+ &lt;h1 className="logo" onClick={<span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >s</span>etSidebarVisible(false)}&gt;</span>
+ 🐱
+ &lt;/h1&gt;
&nbsp;
- return (
- &lt;div className="feed-list"&gt;
- &lt;div className="filter-section"&gt;
- &lt;ul className="filter-list"&gt;
- &lt;li&gt;&lt;Link to="/?filter=unread"&gt;Unread&lt;/Link&gt;&lt;/li&gt;
- &lt;li&gt;&lt;Link to="/?filter=all"&gt;All&lt;/Link&gt;&lt;/li&gt;
- &lt;li&gt;&lt;Link to="/?filter=starred"&gt;Starred&lt;/Link&gt;&lt;/li&gt;
- &lt;/ul&gt;
- &lt;/div&gt;
- &lt;div className="feed-section"&gt;
- &lt;h2&gt;Feeds&lt;/h2&gt;
- {feeds.length === 0 ? (
- &lt;p&gt;No feeds found.&lt;/p&gt;
- ) : (
- &lt;ul className="feed-list-items"&gt;
- {feeds.map((feed) =&gt; (
- &lt;li key={feed._id} className="sidebar-feed-item"&gt;
- &lt;Link to={`/feed/${feed._id}`} className="feed-title"&gt;
- {feed.title || <span class="branch-1 cbranch-no" title="branch not covered" >feed.url}</span>
- &lt;/Link&gt;
- {feed.category &amp;&amp; &lt;span className="feed-category"&gt;{feed.category}&lt;/span&gt;}
- &lt;/li&gt;
- ))}
- &lt;/ul&gt;
- )}
- &lt;/div&gt;
+ &lt;div className="search-section"&gt;
+ &lt;form onSubmit={handleSearch} className="search-form"&gt;
+ &lt;input
+ type="search"
+ placeholder="search..."
+ value={searchQuery}
+ onChange={<span class="fstat-no" title="function not covered" >(e</span>) =&gt; <span class="cstat-no" title="statement not covered" >setSearchQuery(e.target.value)}</span>
+ className="search-input"
+ /&gt;
+ &lt;/form&gt;
+ &lt;/div&gt;
&nbsp;
- {tags &amp;&amp; tags.length &gt; 0 &amp;&amp; (
- &lt;div className="tag-section"&gt;
- &lt;h2&gt;Tags&lt;/h2&gt;
- &lt;ul className="tag-list-items"&gt;
- {tags.map((tag) =&gt; (
- &lt;li key={tag.title} className="tag-item"&gt;
- &lt;Link to={`/tag/${encodeURIComponent(tag.title)}`} className="tag-link"&gt;
- {tag.title}
- &lt;/Link&gt;
- &lt;/li&gt;
- ))}
- &lt;/ul&gt;
- &lt;/div&gt;
- )}
+ &lt;div className="filter-section"&gt;
+ &lt;ul className="filter-list"&gt;
+ &lt;li className="unread_filter"&gt;
+ &lt;Link to="/?filter=unread" className={currentFilter === 'unread' ? 'active' : <span class="branch-1 cbranch-no" title="branch not covered" >''}&gt;</span>
+ unread
+ &lt;/Link&gt;
+ &lt;/li&gt;
+ &lt;li className="all_filter"&gt;
+ &lt;Link to="/?filter=all" className={currentFilter === 'all' ? <span class="branch-0 cbranch-no" title="branch not covered" >'active' : '</span>'}&gt;
+ all
+ &lt;/Link&gt;
+ &lt;/li&gt;
+ &lt;li className="starred_filter"&gt;
+ &lt;Link to="/?filter=starred" className={currentFilter === 'starred' ? <span class="branch-0 cbranch-no" title="branch not covered" >'active' : '</span>'}&gt;
+ starred
+ &lt;/Link&gt;
+ &lt;/li&gt;
+ &lt;/ul&gt;
+ &lt;/div&gt;
+&nbsp;
+ &lt;div className="tag-section"&gt;
+ &lt;h4 onClick={<span class="fstat-no" title="function not covered" >() =&gt; {</span> }} className="section-header"&gt;
+ Tags
+ &lt;/h4&gt;
+ &lt;ul className="tag-list-items"&gt;
+ {tags.map((tag) =&gt; (
+ &lt;li key={tag.title} className="tag-item"&gt;
+ &lt;Link
+ to={`/tag/${encodeURIComponent(tag.title)}`}
+ className={`tag-link ${tagName === tag.title ? <span class="branch-0 cbranch-no" title="branch not covered" >'active' : '</span>'}`}
+ &gt;
+ {tag.title}
+ &lt;/Link&gt;
+ &lt;/li&gt;
+ ))}
+ &lt;/ul&gt;
+ &lt;/div&gt;
+&nbsp;
+ &lt;div className="feed-section"&gt;
+ &lt;h4 onClick={toggleFeeds} className="section-header"&gt;
+ Feeds
+ &lt;/h4&gt;
+ {feedsExpanded &amp;&amp;
+ (feeds.length === 0 ? (
+ &lt;p&gt;No feeds found.&lt;/p&gt;
+ ) : (
+ &lt;ul className="feed-list-items"&gt;
+ {feeds.map((feed) =&gt; (
+ &lt;li key={feed._id} className="sidebar-feed-item"&gt;
+ &lt;Link
+ to={`/feed/${feed._id}`}
+ className={`feed-title ${feedId === String(feed._id) ? <span class="branch-0 cbranch-no" title="branch not covered" >'active' : '</span>'}`}
+ &gt;
+ {feed.title || <span class="branch-1 cbranch-no" title="branch not covered" >feed.url}</span>
+ &lt;/Link&gt;
+ &lt;/li&gt;
+ ))}
+ &lt;/ul&gt;
+ ))}
+ &lt;/div&gt;
+&nbsp;
+ &lt;div className="nav-section"&gt;
+ &lt;ul className="nav-list"&gt;
+ &lt;li&gt;
+ &lt;Link to="/settings" className="nav-link"&gt;
+ settings
+ &lt;/Link&gt;
+ &lt;/li&gt;
+ &lt;li&gt;
+ &lt;button onClick={handleLogout} className="logout-link"&gt;
+ logout
+ &lt;/button&gt;
+ &lt;/li&gt;
+ &lt;/ul&gt;
+ &lt;/div&gt;
+&nbsp;
+ &lt;div className="theme-section"&gt;
+ &lt;div className="theme-selector"&gt;
+ &lt;button
+ onClick={<span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >s</span>etTheme('light')}</span>
+ className={theme === 'light' ? 'active' : <span class="branch-1 cbranch-no" title="branch not covered" >''}</span>
+ title="Light Theme"
+ &gt;
+ ☀️
+ &lt;/button&gt;
+ &lt;button
+ onClick={<span class="fstat-no" title="function not covered" >() =&gt; <span class="cstat-no" title="statement not covered" >s</span>etTheme('dark')}</span>
+ className={theme === 'dark' ? <span class="branch-0 cbranch-no" title="branch not covered" >'active' : '</span>'}
+ title="Dark Theme"
+ &gt;
+ 🌙
+ &lt;/button&gt;
&lt;/div&gt;
- );
+ &lt;/div&gt;
+ &lt;/div&gt;
+ );
}
&nbsp;</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-13T21:49:58.924Z
- </div>
- <script src="../../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../../sorter.js"></script>
- <script src="../../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/components/Login.css.html b/frontend/coverage/src/components/Login.css.html
index bb3654e..031618b 100644
--- a/frontend/coverage/src/components/Login.css.html
+++ b/frontend/coverage/src/components/Login.css.html
@@ -1,64 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/components/Login.css</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 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</a> Login.css
- </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>
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
+ <h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> Login.css</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line low"></div>
- <pre><table class="coverage">
+ </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>
@@ -233,7 +237,7 @@
text-align: center;
}
&nbsp;
-button[type="submit"] {
+button[type='submit'] {
width: 100%;
padding: 0.75rem;
background-color: #007bff;
@@ -245,27 +249,26 @@ button[type="submit"] {
transition: background-color 0.2s;
}
&nbsp;
-button[type="submit"]:hover {
+button[type='submit']:hover {
background-color: #0056b3;
}
&nbsp;</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-13T21:49:58.924Z
- </div>
- <script src="../../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../../sorter.js"></script>
- <script src="../../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/components/Login.tsx.html b/frontend/coverage/src/components/Login.tsx.html
index f29e3cb..43fb613 100644
--- a/frontend/coverage/src/components/Login.tsx.html
+++ b/frontend/coverage/src/components/Login.tsx.html
@@ -1,64 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/components/Login.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 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</a> Login.tsx
- </h1>
- <div class="clearfix">
- <div class="fl pad1y space-right2">
- <span class="strong">100% </span>
- <span class="quiet">Statements</span>
- <span class="fraction">17/17</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">83.33% </span>
- <span class="quiet">Branches</span>
- <span class="fraction">5/6</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">100% </span>
- <span class="quiet">Functions</span>
- <span class="fraction">3/3</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">100% </span>
- <span class="quiet">Lines</span>
- <span class="fraction">17/17</span>
- </div>
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
+ <h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> Login.tsx</h1>
+ <div class='clearfix'>
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">100% </span>
+ <span class="quiet">Statements</span>
+ <span class='fraction'>17/17</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">83.33% </span>
+ <span class="quiet">Branches</span>
+ <span class='fraction'>5/6</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">100% </span>
+ <span class="quiet">Functions</span>
+ <span class='fraction'>3/3</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">100% </span>
+ <span class="quiet">Lines</span>
+ <span class='fraction'>17/17</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line high"></div>
- <pre><table class="coverage">
+ </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>
<a name='L3'></a><a href='#L3'>3</a>
@@ -113,7 +117,11 @@
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<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">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -171,74 +179,75 @@
import { useNavigate } from 'react-router-dom';
import './Login.css';
&nbsp;
+import { apiFetch } from '../utils';
+&nbsp;
export default function Login() {
- const [password, setPassword] = useState('');
- const [error, setError] = useState('');
- const navigate = useNavigate();
+ const [password, setPassword] = useState('');
+ const [error, setError] = useState('');
+ const navigate = useNavigate();
&nbsp;
- const handleSubmit = async (e: FormEvent) =&gt; {
- e.preventDefault();
- setError('');
+ const handleSubmit = async (e: FormEvent) =&gt; {
+ e.preventDefault();
+ setError('');
&nbsp;
- try {
- // Use URLSearchParams to send as form-urlencoded, matching backend expectation
- const params = new URLSearchParams();
- params.append('password', password);
+ try {
+ // Use URLSearchParams to send as form-urlencoded, matching backend expectation
+ const params = new URLSearchParams();
+ params.append('password', password);
&nbsp;
- const res = await fetch('/api/login', {
- method: 'POST',
- body: params,
- });
+ const res = await apiFetch('/api/login', {
+ method: 'POST',
+ body: params,
+ });
&nbsp;
- if (res.ok) {
- navigate('/');
- } else {
- const data = await res.json();
- setError(data.message || <span class="branch-1 cbranch-no" title="branch not covered" >'Login failed')</span>;
- }
- } catch (err) {
- setError('Network error');
- }
- };
+ if (res.ok) {
+ navigate('/');
+ } else {
+ const data = await res.json();
+ setError(data.message || <span class="branch-1 cbranch-no" title="branch not covered" >'Login failed')</span>;
+ }
+ } catch (err) {
+ setError('Network error');
+ }
+ };
&nbsp;
- return (
- &lt;div className="login-container"&gt;
- &lt;form onSubmit={handleSubmit} className="login-form"&gt;
- &lt;h1&gt;neko rss mode&lt;/h1&gt;
- &lt;div className="form-group"&gt;
- &lt;label htmlFor="password"&gt;password&lt;/label&gt;
- &lt;input
- id="password"
- type="password"
- value={password}
- onChange={(e) =&gt; setPassword(e.target.value)}
- autoFocus
- /&gt;
- &lt;/div&gt;
- {error &amp;&amp; &lt;div className="error-message"&gt;{error}&lt;/div&gt;}
- &lt;button type="submit"&gt;login&lt;/button&gt;
- &lt;/form&gt;
+ return (
+ &lt;div className="login-container"&gt;
+ &lt;form onSubmit={handleSubmit} className="login-form"&gt;
+ &lt;h1&gt;neko rss mode&lt;/h1&gt;
+ &lt;div className="form-group"&gt;
+ &lt;label htmlFor="password"&gt;password&lt;/label&gt;
+ &lt;input
+ id="password"
+ type="password"
+ value={password}
+ onChange={(e) =&gt; setPassword(e.target.value)}
+ autoFocus
+ /&gt;
&lt;/div&gt;
- );
+ {error &amp;&amp; &lt;div className="error-message"&gt;{error}&lt;/div&gt;}
+ &lt;button type="submit"&gt;login&lt;/button&gt;
+ &lt;/form&gt;
+ &lt;/div&gt;
+ );
}
&nbsp;</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-13T21:49:58.924Z
- </div>
- <script src="../../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../../sorter.js"></script>
- <script src="../../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/components/Settings.css.html b/frontend/coverage/src/components/Settings.css.html
index 6a1155e..28a1915 100644
--- a/frontend/coverage/src/components/Settings.css.html
+++ b/frontend/coverage/src/components/Settings.css.html
@@ -1,64 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/components/Settings.css</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 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</a> Settings.css
- </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>
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
+ <h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> Settings.css</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line low"></div>
- <pre><table class="coverage">
+ </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>
@@ -141,7 +145,163 @@
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -224,105 +384,182 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">.settings-page {
- padding: 2rem;
- max-width: 800px;
- margin: 0 auto;
+ padding: 2rem;
+ max-width: 800px;
+ margin: 0 auto;
}
&nbsp;
.add-feed-section {
- background: #f9f9f9;
- padding: 1.5rem;
- border-radius: 8px;
- margin-bottom: 2rem;
- border: 1px solid #eee;
+ background: var(--sidebar-bg);
+ padding: 1.5rem;
+ border-radius: 8px;
+ margin-bottom: 2rem;
+ border: 1px solid var(--border-color);
}
&nbsp;
.add-feed-form {
- display: flex;
- gap: 1rem;
+ display: flex;
+ gap: 1rem;
}
&nbsp;
.feed-input {
- flex: 1;
- padding: 0.5rem;
- border: 1px solid #ccc;
- border-radius: 4px;
- font-size: 1rem;
+ flex: 1;
+ padding: 0.5rem;
+ border: 1px solid var(--border-color);
+ background: var(--bg-color);
+ color: var(--text-color);
+ border-radius: 4px;
+ font-size: 1rem;
}
&nbsp;
.error-message {
- color: #d32f2f;
- margin-top: 1rem;
+ color: #d32f2f;
+ margin-top: 1rem;
}
&nbsp;
.settings-feed-list {
- list-style: none;
- padding: 0;
- border: 1px solid #eee;
- border-radius: 8px;
+ list-style: none;
+ padding: 0;
+ border: 1px solid var(--border-color);
+ border-radius: 8px;
}
&nbsp;
.settings-feed-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 1rem;
- border-bottom: 1px solid #eee;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem;
+ border-bottom: 1px solid var(--border-color);
}
&nbsp;
.settings-feed-item:last-child {
- border-bottom: none;
+ border-bottom: none;
}
&nbsp;
.feed-info {
- display: flex;
- flex-direction: column;
+ display: flex;
+ flex-direction: column;
}
&nbsp;
.feed-title {
- font-weight: bold;
- font-size: 1.1rem;
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+ font-weight: bold;
+ font-size: 1.1rem;
}
&nbsp;
.feed-url {
- color: #666;
- font-size: 0.9rem;
+ color: var(--text-color);
+ opacity: 0.6;
+ font-size: 0.9rem;
}
&nbsp;
.delete-btn {
- background: #ff5252;
- color: white;
- border: none;
- padding: 0.5rem 1rem;
- border-radius: 4px;
- cursor: pointer;
+ background: #ff5252;
+ color: white;
+ border: none;
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+ cursor: pointer;
}
&nbsp;
.delete-btn:hover {
- background: #ff1744;
+ background: #ff1744;
}
&nbsp;
.delete-btn:disabled {
- background: #ffcdd2;
- cursor: not-allowed;
+ background: #ffcdd2;
+ cursor: not-allowed;
+}
+&nbsp;
+.import-export-section {
+ display: flex;
+ gap: 2rem;
+ margin-bottom: 2rem;
+}
+&nbsp;
+@media (max-width: 600px) {
+ .import-export-section {
+ flex-direction: column;
+ }
+}
+&nbsp;
+.import-section,
+.export-section {
+ flex: 1;
+ background: var(--sidebar-bg);
+ padding: 1.5rem;
+ border-radius: 8px;
+ border: 1px solid var(--border-color);
+}
+&nbsp;
+.import-form {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+&nbsp;
+.file-input {
+ font-size: 0.9rem;
+ max-width: 100%;
+}
+&nbsp;
+.export-buttons {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+}
+&nbsp;
+.export-btn {
+ display: inline-block;
+ padding: 0.5rem 1rem;
+ background: var(--bg-color);
+ color: var(--link-color);
+ text-decoration: none;
+ border: 1px solid var(--border-color);
+ border-radius: 4px;
+ font-weight: bold;
+ text-align: center;
+ min-width: 70px;
+}
+&nbsp;
+.export-btn:hover {
+ background: var(--sidebar-bg);
+}
+&nbsp;
+button {
+ cursor: pointer;
+ padding: 0.5rem 1rem;
+ border-radius: 4px;
+ border: 1px solid var(--border-color);
+ background: var(--bg-color);
+ color: var(--text-color);
+ font-weight: bold;
+}
+&nbsp;
+button:hover:not(:disabled) {
+ background: var(--sidebar-bg);
+}
+&nbsp;
+button:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
}</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-13T21:49:58.924Z
- </div>
- <script src="../../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../../sorter.js"></script>
- <script src="../../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/components/Settings.tsx.html b/frontend/coverage/src/components/Settings.tsx.html
index df6d027..3d8d219 100644
--- a/frontend/coverage/src/components/Settings.tsx.html
+++ b/frontend/coverage/src/components/Settings.tsx.html
@@ -1,64 +1,68 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/components/Settings.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 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</a> Settings.tsx
- </h1>
- <div class="clearfix">
- <div class="fl pad1y space-right2">
- <span class="strong">75.55% </span>
- <span class="quiet">Statements</span>
- <span class="fraction">34/45</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">56.25% </span>
- <span class="quiet">Branches</span>
- <span class="fraction">9/16</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">82.35% </span>
- <span class="quiet">Functions</span>
- <span class="fraction">14/17</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">84.61% </span>
- <span class="quiet">Lines</span>
- <span class="fraction">33/39</span>
- </div>
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
+ <h1><a href="../../index.html">All files</a> / <a href="index.html">src/components</a> Settings.tsx</h1>
+ <div class='clearfix'>
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">56.25% </span>
+ <span class="quiet">Statements</span>
+ <span class='fraction'>36/64</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">41.66% </span>
+ <span class="quiet">Branches</span>
+ <span class='fraction'>10/24</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">63.63% </span>
+ <span class="quiet">Functions</span>
+ <span class='fraction'>14/22</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">62.5% </span>
+ <span class="quiet">Lines</span>
+ <span class='fraction'>35/56</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line medium"></div>
- <pre><table class="coverage">
+ </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>
<a name='L3'></a><a href='#L3'>3</a>
@@ -180,7 +184,67 @@
<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<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></td><td class="line-coverage quiet"><span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -191,8 +255,6 @@
<span class="cline-any cline-yes">14x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">14x</span>
-<span class="cline-any cline-yes">3x</span>
-<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">14x</span>
<span class="cline-any cline-yes">4x</span>
@@ -212,6 +274,10 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">14x</span>
+<span class="cline-any cline-yes">3x</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">14x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-yes">1x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -254,6 +320,34 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">14x</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-yes">14x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -281,6 +375,34 @@
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-no">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
+<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-yes">5x</span>
<span class="cline-any cline-neutral">&nbsp;</span>
<span class="cline-any cline-neutral">&nbsp;</span>
@@ -304,142 +426,200 @@
<span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">import React, { useEffect, useState } from 'react';
import type { Feed } from '../types';
import './Settings.css';
+import { apiFetch } from '../utils';
&nbsp;
export default function Settings() {
- const [feeds, setFeeds] = useState&lt;Feed[]&gt;([]);
- const [newFeedUrl, setNewFeedUrl] = useState('');
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState&lt;string | null&gt;(null);
+ const [feeds, setFeeds] = useState&lt;Feed[]&gt;([]);
+ const [newFeedUrl, setNewFeedUrl] = useState('');
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState&lt;string | null&gt;(null);
&nbsp;
- useEffect(() =&gt; {
+ const [importFile, setImportFile] = useState&lt;File | null&gt;(null);
+&nbsp;
+ const fetchFeeds = () =&gt; {
+ setLoading(true);
+ apiFetch('/api/feed/')
+ .then((res) =&gt; {
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to fetch feeds');</span>
+ return res.json();
+ })
+ .then((data) =&gt; {
+ setFeeds(data);
+ setLoading(false);
+ })
+ .catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; {
+<span class="cstat-no" title="statement not covered" > setError(err.message);</span>
+<span class="cstat-no" title="statement not covered" > setLoading(false);</span>
+ });
+ };
+&nbsp;
+ useEffect(() =&gt; {
+ fetchFeeds();
+ }, []);
+&nbsp;
+ const handleAddFeed = (e: React.FormEvent) =&gt; {
+ e.preventDefault();
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!newFeedUrl) <span class="cstat-no" title="statement not covered" >return;</span>
+&nbsp;
+ setLoading(true);
+ apiFetch('/api/feed/', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ url: newFeedUrl }),
+ })
+ .then((res) =&gt; {
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to add feed');</span>
+ return res.json();
+ })
+ .then(() =&gt; {
+ setNewFeedUrl('');
fetchFeeds();
- }, []);
+ })
+ .catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; {
+<span class="cstat-no" title="statement not covered" > setError(err.message);</span>
+<span class="cstat-no" title="statement not covered" > setLoading(false);</span>
+ });
+ };
+&nbsp;
+ const handleDeleteFeed = (id: number) =&gt; {
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!globalThis.confirm('Are you sure you want to delete this feed?')) <span class="cstat-no" title="statement not covered" >return;</span>
&nbsp;
- const fetchFeeds = () =&gt; {
- setLoading(true);
- fetch('/api/feed/')
- .then((res) =&gt; {
- <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to fetch feeds');</span>
- return res.json();
- })
- .then((data) =&gt; {
- setFeeds(data);
- setLoading(false);
- })
- .catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; {
-<span class="cstat-no" title="statement not covered" > setError(err.message);</span>
-<span class="cstat-no" title="statement not covered" > setLoading(false);</span>
- });
- };
+ setLoading(true);
+ apiFetch(`/api/feed/${id}`, {
+ method: 'DELETE',
+ })
+ .then((res) =&gt; {
+ <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to delete feed');</span>
+ setFeeds(feeds.filter((f) =&gt; f._id !== id));
+ setLoading(false);
+ })
+ .catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; {
+<span class="cstat-no" title="statement not covered" > setError(err.message);</span>
+<span class="cstat-no" title="statement not covered" > setLoading(false);</span>
+ });
+ };
&nbsp;
- const handleAddFeed = (e: React.FormEvent) =&gt; {
- e.preventDefault();
- <span class="missing-if-branch" title="if path not taken" >I</span>if (!newFeedUrl) <span class="cstat-no" title="statement not covered" >return;</span>
+ const handleImport = <span class="fstat-no" title="function not covered" >(e</span>: React.FormEvent) =&gt; {
+<span class="cstat-no" title="statement not covered" > e.preventDefault();</span>
+<span class="cstat-no" title="statement not covered" > if (!importFile) <span class="cstat-no" title="statement not covered" >return;</span></span>
&nbsp;
- setLoading(true);
- fetch('/api/feed/', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ url: newFeedUrl }),
- })
- .then((res) =&gt; {
- <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to add feed');</span>
- return res.json();
- })
- .then(() =&gt; {
- setNewFeedUrl('');
- fetchFeeds(); // Refresh list (or we could append if server returns full feed object)
- })
- .catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; {
-<span class="cstat-no" title="statement not covered" > setError(err.message);</span>
-<span class="cstat-no" title="statement not covered" > setLoading(false);</span>
- });
- };
+<span class="cstat-no" title="statement not covered" > setLoading(true);</span>
+ const formData = <span class="cstat-no" title="statement not covered" >new FormData();</span>
+<span class="cstat-no" title="statement not covered" > formData.append('file', importFile);</span>
+<span class="cstat-no" title="statement not covered" > formData.append('format', 'opml');</span>
&nbsp;
- const handleDeleteFeed = (id: number) =&gt; {
- <span class="missing-if-branch" title="if path not taken" >I</span>if (!globalThis.confirm('Are you sure you want to delete this feed?')) <span class="cstat-no" title="statement not covered" >return;</span>
+<span class="cstat-no" title="statement not covered" > apiFetch('/api/import', {</span>
+ method: 'POST',
+ body: formData,
+ })
+ .then(<span class="fstat-no" title="function not covered" >(r</span>es) =&gt; {
+<span class="cstat-no" title="statement not covered" > if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to import feeds');</span></span>
+<span class="cstat-no" title="statement not covered" > return res.json();</span>
+ })
+ .then(<span class="fstat-no" title="function not covered" >() =&gt; {</span>
+<span class="cstat-no" title="statement not covered" > setImportFile(null);</span>
+<span class="cstat-no" title="statement not covered" > fetchFeeds();</span>
+<span class="cstat-no" title="statement not covered" > alert('Import successful!');</span>
+ })
+ .catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; {
+<span class="cstat-no" title="statement not covered" > setError(err.message);</span>
+<span class="cstat-no" title="statement not covered" > setLoading(false);</span>
+ });
+ };
&nbsp;
- setLoading(true);
- fetch(`/api/feed/${id}`, {
- method: 'DELETE',
- })
- .then((res) =&gt; {
- <span class="missing-if-branch" title="if path not taken" >I</span>if (!res.ok) <span class="cstat-no" title="statement not covered" >throw new Error('Failed to delete feed');</span>
- setFeeds(feeds.filter((f) =&gt; f._id !== id));
- setLoading(false);
- })
- .catch(<span class="fstat-no" title="function not covered" >(e</span>rr) =&gt; {
-<span class="cstat-no" title="statement not covered" > setError(err.message);</span>
-<span class="cstat-no" title="statement not covered" > setLoading(false);</span>
- });
- };
+ return (
+ &lt;div className="settings-page"&gt;
+ &lt;h2&gt;Settings&lt;/h2&gt;
&nbsp;
- return (
- &lt;div className="settings-page"&gt;
- &lt;h2&gt;Settings&lt;/h2&gt;
+ &lt;div className="add-feed-section"&gt;
+ &lt;h3&gt;Add New Feed&lt;/h3&gt;
+ &lt;form onSubmit={handleAddFeed} className="add-feed-form"&gt;
+ &lt;input
+ type="url"
+ value={newFeedUrl}
+ onChange={(e) =&gt; setNewFeedUrl(e.target.value)}
+ placeholder="https://example.com/feed.xml"
+ required
+ className="feed-input"
+ disabled={loading}
+ /&gt;
+ &lt;button type="submit" disabled={loading}&gt;
+ Add Feed
+ &lt;/button&gt;
+ &lt;/form&gt;
+ &lt;/div&gt;
&nbsp;
- &lt;div className="add-feed-section"&gt;
- &lt;h3&gt;Add New Feed&lt;/h3&gt;
- &lt;form onSubmit={handleAddFeed} className="add-feed-form"&gt;
- &lt;input
- type="url"
- value={newFeedUrl}
- onChange={(e) =&gt; setNewFeedUrl(e.target.value)}
- placeholder="https://example.com/feed.xml"
- required
- className="feed-input"
- disabled={loading}
- /&gt;
- &lt;button type="submit" disabled={loading}&gt;
- Add Feed
- &lt;/button&gt;
- &lt;/form&gt;
- {error &amp;&amp; <span class="branch-1 cbranch-no" title="branch not covered" >&lt;p className="error-message"&gt;{error}&lt;/p&gt;}</span>
- &lt;/div&gt;
+ &lt;div className="import-export-section"&gt;
+ &lt;div className="import-section"&gt;
+ &lt;h3&gt;Import Feeds (OPML)&lt;/h3&gt;
+ &lt;form onSubmit={handleImport} className="import-form"&gt;
+ &lt;input
+ type="file"
+ accept=".opml,.xml,.txt"
+ onChange={<span class="fstat-no" title="function not covered" >(e</span>) =&gt; <span class="cstat-no" title="statement not covered" >setImportFile(e.target.files?.[0] || null)}</span>
+ className="file-input"
+ disabled={loading}
+ /&gt;
+ &lt;button type="submit" disabled={!importFile || <span class="branch-1 cbranch-no" title="branch not covered" >loading}&gt;</span>
+ Import
+ &lt;/button&gt;
+ &lt;/form&gt;
+ &lt;/div&gt;
&nbsp;
- &lt;div className="feed-list-section"&gt;
- &lt;h3&gt;Manage Feeds&lt;/h3&gt;
- {loading &amp;&amp; &lt;p&gt;Loading...&lt;/p&gt;}
- &lt;ul className="settings-feed-list"&gt;
- {feeds.map((feed) =&gt; (
- &lt;li key={feed._id} className="settings-feed-item"&gt;
- &lt;div className="feed-info"&gt;
- &lt;span className="feed-title"&gt;{feed.title || <span class="branch-1 cbranch-no" title="branch not covered" >'(No Title)'}&lt;</span>/span&gt;
- &lt;span className="feed-url"&gt;{feed.url}&lt;/span&gt;
- &lt;/div&gt;
- &lt;button
- onClick={() =&gt; handleDeleteFeed(feed._id)}
- className="delete-btn"
- disabled={loading}
- title="Delete Feed"
- &gt;
- Delete
- &lt;/button&gt;
- &lt;/li&gt;
- ))}
- &lt;/ul&gt;
- &lt;/div&gt;
+ &lt;div className="export-section"&gt;
+ &lt;h3&gt;Export Feeds&lt;/h3&gt;
+ &lt;div className="export-buttons"&gt;
+ &lt;a href="/api/export/opml" className="export-btn"&gt;OPML&lt;/a&gt;
+ &lt;a href="/api/export/text" className="export-btn"&gt;Text&lt;/a&gt;
+ &lt;a href="/api/export/json" className="export-btn"&gt;JSON&lt;/a&gt;
+ &lt;/div&gt;
&lt;/div&gt;
- );
+ &lt;/div&gt;
+&nbsp;
+ {error &amp;&amp; <span class="branch-1 cbranch-no" title="branch not covered" >&lt;p className="error-message"&gt;{error}&lt;/p&gt;}</span>
+&nbsp;
+ &lt;div className="feed-list-section"&gt;
+ &lt;h3&gt;Manage Feeds&lt;/h3&gt;
+ {loading &amp;&amp; &lt;p&gt;Loading...&lt;/p&gt;}
+ &lt;ul className="settings-feed-list"&gt;
+ {feeds.map((feed) =&gt; (
+ &lt;li key={feed._id} className="settings-feed-item"&gt;
+ &lt;div className="feed-info"&gt;
+ &lt;span className="feed-title"&gt;{feed.title || <span class="branch-1 cbranch-no" title="branch not covered" >'(No Title)'}&lt;</span>/span&gt;
+ &lt;span className="feed-url"&gt;{feed.url}&lt;/span&gt;
+ &lt;/div&gt;
+ &lt;button
+ onClick={() =&gt; handleDeleteFeed(feed._id)}
+ className="delete-btn"
+ disabled={loading}
+ title="Delete Feed"
+ &gt;
+ Delete
+ &lt;/button&gt;
+ &lt;/li&gt;
+ ))}
+ &lt;/ul&gt;
+ &lt;/div&gt;
+ &lt;/div&gt;
+ );
}
&nbsp;</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-13T21:49:58.924Z
- </div>
- <script src="../../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../../sorter.js"></script>
- <script src="../../block-navigation.js"></script>
- </body>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/components/index.html b/frontend/coverage/src/components/index.html
index 7e1a0b7..66ca900 100644
--- a/frontend/coverage/src/components/index.html
+++ b/frontend/coverage/src/components/index.html
@@ -1,303 +1,251 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src/components</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 type='text/css'>
+ .coverage-summary .sorter {
+ background-image: url(../../sort-arrow-sprite.png);
+ }
</style>
- </head>
-
- <body>
- <div class="wrapper">
- <div class="pad1">
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
<h1><a href="../../index.html">All files</a> src/components</h1>
- <div class="clearfix">
- <div class="fl pad1y space-right2">
- <span class="strong">86.78% </span>
- <span class="quiet">Statements</span>
- <span class="fraction">197/227</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">77.2% </span>
- <span class="quiet">Branches</span>
- <span class="fraction">105/136</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">87.5% </span>
- <span class="quiet">Functions</span>
- <span class="fraction">56/64</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">89.26% </span>
- <span class="quiet">Lines</span>
- <span class="fraction">183/205</span>
- </div>
+ <div class='clearfix'>
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">79.57% </span>
+ <span class="quiet">Statements</span>
+ <span class='fraction'>226/284</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">70.17% </span>
+ <span class="quiet">Branches</span>
+ <span class='fraction'>120/171</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">75.9% </span>
+ <span class="quiet">Functions</span>
+ <span class='fraction'>63/83</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">81.78% </span>
+ <span class="quiet">Lines</span>
+ <span class='fraction'>211/258</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line high"></div>
- <div class="pad1">
- <table class="coverage-summary">
- <thead>
- <tr>
- <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
- <th
- data-col="pic"
- data-type="number"
- data-fmt="html"
- data-html="true"
- class="pic"
- ></th>
- <th data-col="statements" data-type="number" data-fmt="pct" class="pct">
- Statements
- </th>
- <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
- <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
- <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
- <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
- <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
- <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
- <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="file empty" data-value="FeedItem.css">
- <a href="FeedItem.css.html">FeedItem.css</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>
- </tr>
+ </div>
+ <div class='status-line medium'></div>
+ <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+ <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+ <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+ <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+ <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+ <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+ <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+ <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+ <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+ <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+ <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+ <td class="file empty" data-value="FeedItem.css"><a href="FeedItem.css.html">FeedItem.css</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>
+ </tr>
- <tr>
- <td class="file medium" data-value="FeedItem.tsx">
- <a href="FeedItem.tsx.html">FeedItem.tsx</a>
- </td>
- <td data-value="78.94" class="pic medium">
- <div class="chart">
- <div class="cover-fill" style="width: 78%"></div>
- <div class="cover-empty" style="width: 22%"></div>
- </div>
- </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.88" class="pct high">88.88%</td>
- <td data-value="18" class="abs high">16/18</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="78.94" class="pct medium">78.94%</td>
- <td data-value="19" class="abs medium">15/19</td>
- </tr>
+<tr>
+ <td class="file medium" data-value="FeedItem.tsx"><a href="FeedItem.tsx.html">FeedItem.tsx</a></td>
+ <td data-value="78.12" class="pic medium">
+ <div class="chart"><div class="cover-fill" style="width: 78%"></div><div class="cover-empty" style="width: 22%"></div></div>
+ </td>
+ <td data-value="78.12" class="pct medium">78.12%</td>
+ <td data-value="32" class="abs medium">25/32</td>
+ <td data-value="86.95" class="pct high">86.95%</td>
+ <td data-value="23" class="abs high">20/23</td>
+ <td data-value="83.33" class="pct high">83.33%</td>
+ <td data-value="12" class="abs high">10/12</td>
+ <td data-value="80.64" class="pct high">80.64%</td>
+ <td data-value="31" class="abs high">25/31</td>
+ </tr>
- <tr>
- <td class="file empty" data-value="FeedItems.css">
- <a href="FeedItems.css.html">FeedItems.css</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>
- </tr>
+<tr>
+ <td class="file empty" data-value="FeedItems.css"><a href="FeedItems.css.html">FeedItems.css</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>
+ </tr>
- <tr>
- <td class="file high" data-value="FeedItems.tsx">
- <a href="FeedItems.tsx.html">FeedItems.tsx</a>
- </td>
- <td data-value="89.34" class="pic high">
- <div class="chart">
- <div class="cover-fill" style="width: 89%"></div>
- <div class="cover-empty" style="width: 11%"></div>
- </div>
- </td>
- <td data-value="89.34" class="pct high">89.34%</td>
- <td data-value="122" class="abs high">109/122</td>
- <td data-value="77.21" class="pct medium">77.21%</td>
- <td data-value="79" class="abs medium">61/79</td>
- <td data-value="86.2" class="pct high">86.2%</td>
- <td data-value="29" class="abs high">25/29</td>
- <td data-value="89.09" class="pct high">89.09%</td>
- <td data-value="110" class="abs high">98/110</td>
- </tr>
+<tr>
+ <td class="file high" data-value="FeedItems.tsx"><a href="FeedItems.tsx.html">FeedItems.tsx</a></td>
+ <td data-value="88.97" 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.97" class="pct high">88.97%</td>
+ <td data-value="127" class="abs high">113/127</td>
+ <td data-value="75.3" class="pct medium">75.3%</td>
+ <td data-value="81" class="abs medium">61/81</td>
+ <td data-value="86.2" class="pct high">86.2%</td>
+ <td data-value="29" class="abs high">25/29</td>
+ <td data-value="88.69" class="pct high">88.69%</td>
+ <td data-value="115" class="abs high">102/115</td>
+ </tr>
- <tr>
- <td class="file empty" data-value="FeedList.css">
- <a href="FeedList.css.html">FeedList.css</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>
- </tr>
+<tr>
+ <td class="file empty" data-value="FeedList.css"><a href="FeedList.css.html">FeedList.css</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>
+ </tr>
- <tr>
- <td class="file high" data-value="FeedList.tsx">
- <a href="FeedList.tsx.html">FeedList.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="91.66" class="pct high">91.66%</td>
- <td data-value="24" class="abs high">22/24</td>
- <td data-value="82.35" class="pct high">82.35%</td>
- <td data-value="17" class="abs high">14/17</td>
- <td data-value="100" class="pct high">100%</td>
- <td data-value="8" class="abs high">8/8</td>
- <td data-value="100" class="pct high">100%</td>
- <td data-value="20" class="abs high">20/20</td>
- </tr>
+<tr>
+ <td class="file medium" data-value="FeedList.tsx"><a href="FeedList.tsx.html">FeedList.tsx</a></td>
+ <td data-value="79.54" class="pic medium">
+ <div class="chart"><div class="cover-fill" style="width: 79%"></div><div class="cover-empty" style="width: 21%"></div></div>
+ </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="64.86" class="pct medium">64.86%</td>
+ <td data-value="37" class="abs medium">24/37</td>
+ <td data-value="64.7" class="pct medium">64.7%</td>
+ <td data-value="17" class="abs medium">11/17</td>
+ <td data-value="82.05" class="pct high">82.05%</td>
+ <td data-value="39" class="abs high">32/39</td>
+ </tr>
- <tr>
- <td class="file empty" data-value="Login.css">
- <a href="Login.css.html">Login.css</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>
- </tr>
+<tr>
+ <td class="file empty" data-value="Login.css"><a href="Login.css.html">Login.css</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>
+ </tr>
- <tr>
- <td class="file high" data-value="Login.tsx">
- <a href="Login.tsx.html">Login.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="17" class="abs high">17/17</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="17" class="abs high">17/17</td>
- </tr>
+<tr>
+ <td class="file high" data-value="Login.tsx"><a href="Login.tsx.html">Login.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="17" class="abs high">17/17</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="17" class="abs high">17/17</td>
+ </tr>
- <tr>
- <td class="file empty" data-value="Settings.css">
- <a href="Settings.css.html">Settings.css</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>
- </tr>
+<tr>
+ <td class="file empty" data-value="Settings.css"><a href="Settings.css.html">Settings.css</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>
+ </tr>
- <tr>
- <td class="file medium" data-value="Settings.tsx">
- <a href="Settings.tsx.html">Settings.tsx</a>
- </td>
- <td data-value="75.55" 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.55" class="pct medium">75.55%</td>
- <td data-value="45" class="abs medium">34/45</td>
- <td data-value="56.25" class="pct medium">56.25%</td>
- <td data-value="16" class="abs medium">9/16</td>
- <td data-value="82.35" class="pct high">82.35%</td>
- <td data-value="17" class="abs high">14/17</td>
- <td data-value="84.61" class="pct high">84.61%</td>
- <td data-value="39" class="abs high">33/39</td>
- </tr>
- </tbody>
- </table>
- </div>
- <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-13T21:49:58.924Z
- </div>
- <script src="../../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../../sorter.js"></script>
- <script src="../../block-navigation.js"></script>
- </body>
+<tr>
+ <td class="file medium" data-value="Settings.tsx"><a href="Settings.tsx.html">Settings.tsx</a></td>
+ <td data-value="56.25" class="pic medium">
+ <div class="chart"><div class="cover-fill" style="width: 56%"></div><div class="cover-empty" style="width: 44%"></div></div>
+ </td>
+ <td data-value="56.25" class="pct medium">56.25%</td>
+ <td data-value="64" class="abs medium">36/64</td>
+ <td data-value="41.66" class="pct low">41.66%</td>
+ <td data-value="24" class="abs low">10/24</td>
+ <td data-value="63.63" class="pct medium">63.63%</td>
+ <td data-value="22" class="abs medium">14/22</td>
+ <td data-value="62.5" class="pct medium">62.5%</td>
+ <td data-value="56" class="abs medium">35/56</td>
+ </tr>
+
+</tbody>
+</table>
+</div>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/coverage/src/index.html b/frontend/coverage/src/index.html
index 14e9f51..19a8dfd 100644
--- a/frontend/coverage/src/index.html
+++ b/frontend/coverage/src/index.html
@@ -1,139 +1,146 @@
+
<!doctype html>
<html lang="en">
- <head>
+
+<head>
<title>Code coverage report for src</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 type='text/css'>
+ .coverage-summary .sorter {
+ background-image: url(../sort-arrow-sprite.png);
+ }
</style>
- </head>
-
- <body>
- <div class="wrapper">
- <div class="pad1">
+</head>
+
+<body>
+<div class='wrapper'>
+ <div class='pad1'>
<h1><a href="../index.html">All files</a> src</h1>
- <div class="clearfix">
- <div class="fl pad1y space-right2">
- <span class="strong">78.94% </span>
- <span class="quiet">Statements</span>
- <span class="fraction">15/19</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">66.66% </span>
- <span class="quiet">Branches</span>
- <span class="fraction">4/6</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">77.77% </span>
- <span class="quiet">Functions</span>
- <span class="fraction">7/9</span>
- </div>
-
- <div class="fl pad1y space-right2">
- <span class="strong">78.94% </span>
- <span class="quiet">Lines</span>
- <span class="fraction">15/19</span>
- </div>
+ <div class='clearfix'>
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">76.47% </span>
+ <span class="quiet">Statements</span>
+ <span class='fraction'>26/34</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">69.23% </span>
+ <span class="quiet">Branches</span>
+ <span class='fraction'>18/26</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">70% </span>
+ <span class="quiet">Functions</span>
+ <span class='fraction'>7/10</span>
+ </div>
+
+
+ <div class='fl pad1y space-right2'>
+ <span class="strong">78.78% </span>
+ <span class="quiet">Lines</span>
+ <span class='fraction'>26/33</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.
+ 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>
+ <div class="quiet">
+ Filter:
+ <input type="search" id="fileSearch">
+ </div>
</template>
- </div>
- <div class="status-line medium"></div>
- <div class="pad1">
- <table class="coverage-summary">
- <thead>
- <tr>
- <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
- <th
- data-col="pic"
- data-type="number"
- data-fmt="html"
- data-html="true"
- class="pic"
- ></th>
- <th data-col="statements" data-type="number" data-fmt="pct" class="pct">
- Statements
- </th>
- <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
- <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
- <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
- <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
- <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
- <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
- <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td class="file empty" data-value="App.css"><a href="App.css.html">App.css</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>
- </tr>
-
- <tr>
- <td class="file medium" data-value="App.tsx"><a href="App.tsx.html">App.tsx</a></td>
- <td data-value="78.94" class="pic medium">
- <div class="chart">
- <div class="cover-fill" style="width: 78%"></div>
- <div class="cover-empty" style="width: 22%"></div>
- </div>
- </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="66.66" class="pct medium">66.66%</td>
- <td data-value="6" class="abs medium">4/6</td>
- <td data-value="77.77" class="pct medium">77.77%</td>
- <td data-value="9" class="abs medium">7/9</td>
- <td data-value="78.94" class="pct medium">78.94%</td>
- <td data-value="19" class="abs medium">15/19</td>
- </tr>
- </tbody>
- </table>
- </div>
- <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-13T21:49:58.924Z
</div>
- <script src="../prettify.js"></script>
- <script>
- window.onload = function () {
- prettyPrint();
- };
- </script>
- <script src="../sorter.js"></script>
- <script src="../block-navigation.js"></script>
- </body>
+ <div class='status-line medium'></div>
+ <div class="pad1">
+<table class="coverage-summary">
+<thead>
+<tr>
+ <th data-col="file" data-fmt="html" data-html="true" class="file">File</th>
+ <th data-col="pic" data-type="number" data-fmt="html" data-html="true" class="pic"></th>
+ <th data-col="statements" data-type="number" data-fmt="pct" class="pct">Statements</th>
+ <th data-col="statements_raw" data-type="number" data-fmt="html" class="abs"></th>
+ <th data-col="branches" data-type="number" data-fmt="pct" class="pct">Branches</th>
+ <th data-col="branches_raw" data-type="number" data-fmt="html" class="abs"></th>
+ <th data-col="functions" data-type="number" data-fmt="pct" class="pct">Functions</th>
+ <th data-col="functions_raw" data-type="number" data-fmt="html" class="abs"></th>
+ <th data-col="lines" data-type="number" data-fmt="pct" class="pct">Lines</th>
+ <th data-col="lines_raw" data-type="number" data-fmt="html" class="abs"></th>
+</tr>
+</thead>
+<tbody><tr>
+ <td class="file empty" data-value="App.css"><a href="App.css.html">App.css</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>
+ </tr>
+
+<tr>
+ <td class="file medium" data-value="App.tsx"><a href="App.tsx.html">App.tsx</a></td>
+ <td data-value="72.72" class="pic medium">
+ <div class="chart"><div class="cover-fill" style="width: 72%"></div><div class="cover-empty" style="width: 28%"></div></div>
+ </td>
+ <td data-value="72.72" class="pct medium">72.72%</td>
+ <td data-value="22" class="abs medium">16/22</td>
+ <td data-value="62.5" class="pct medium">62.5%</td>
+ <td data-value="16" class="abs medium">10/16</td>
+ <td data-value="62.5" class="pct medium">62.5%</td>
+ <td data-value="8" class="abs medium">5/8</td>
+ <td data-value="72.72" class="pct medium">72.72%</td>
+ <td data-value="22" class="abs medium">16/22</td>
+ </tr>
+
+<tr>
+ <td class="file high" data-value="utils.ts"><a href="utils.ts.html">utils.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="12" class="abs high">10/12</td>
+ <td data-value="80" class="pct high">80%</td>
+ <td data-value="10" class="abs high">8/10</td>
+ <td data-value="100" class="pct high">100%</td>
+ <td data-value="2" class="abs high">2/2</td>
+ <td data-value="90.9" class="pct high">90.9%</td>
+ <td data-value="11" class="abs high">10/11</td>
+ </tr>
+
+</tbody>
+</table>
+</div>
+ <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-14T18:02:09.004Z
+ </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>
+ \ No newline at end of file
diff --git a/frontend/src/components/Settings.test.tsx b/frontend/src/components/Settings.test.tsx
index f46ce6f..b7de3bb 100644
--- a/frontend/src/components/Settings.test.tsx
+++ b/frontend/src/components/Settings.test.tsx
@@ -98,4 +98,42 @@ describe('Settings Component', () => {
expect(screen.queryByText('Tech News')).not.toBeInTheDocument();
});
});
+
+ it('imports an OPML file', async () => {
+ (global.fetch as any)
+ .mockResolvedValueOnce({ ok: true, json: async () => [] }) // Initial load
+ .mockResolvedValueOnce({ ok: true, json: async () => ({ status: 'ok' }) }) // Import
+ .mockResolvedValueOnce({
+ ok: true,
+ json: async () => [{ _id: 1, title: 'Imported Feed', url: 'http://imported.com/rss' }],
+ }); // Refresh load
+
+ render(<Settings />);
+
+ const file = new File(['<opml>...</opml>'], 'feeds.opml', { type: 'text/xml' });
+ const fileInput = screen.getByLabelText(/import feeds/i, { selector: 'input[type="file"]' });
+ const importButton = screen.getByText('Import');
+
+ fireEvent.change(fileInput, { target: { files: [file] } });
+ await waitFor(() => {
+ expect(importButton).not.toBeDisabled();
+ });
+
+ fireEvent.click(importButton);
+
+ await waitFor(() => {
+ expect(global.fetch).toHaveBeenCalledWith(
+ '/api/import',
+ expect.objectContaining({
+ method: 'POST',
+ body: expect.any(FormData),
+ })
+ );
+ });
+
+ // Check if refresh happens
+ await waitFor(() => {
+ expect(screen.getByText('Imported Feed')).toBeInTheDocument();
+ });
+ });
});
diff --git a/frontend/src/components/Settings.tsx b/frontend/src/components/Settings.tsx
index 6b6dab1..16cf6a3 100644
--- a/frontend/src/components/Settings.tsx
+++ b/frontend/src/components/Settings.tsx
@@ -131,6 +131,7 @@ export default function Settings() {
<input
type="file"
accept=".opml,.xml,.txt"
+ aria-label="Import Feeds"
onChange={(e) => setImportFile(e.target.files?.[0] || null)}
className="file-input"
disabled={loading}