From 5cf8275540d7162cd4936a7c0e76dbfe7f66b62c Mon Sep 17 00:00:00 2001
From: Adam Mathes <adam@adammathes.com>
Date: Mon, 16 Feb 2026 10:53:59 -0800
Subject: V3 UI Polish: Improved keyboard navigation, fixed logo position, and
 updated branding

- Fix V3 keyboard navigation delay (resolved NK-wjats7)
- Update V3 document title to 'neko' (resolved NK-4p3s91)
- Fix V3 neko logo/button position to be top-left fixed (resolved NK-89za3s)
- Improve FeedItems (React) stability with ref-based index tracking and robust tests
- Sync V3 styling and selection feedback with V2 patterns
- Rebuild production assets
---
 frontend/src/components/FeedItems.tsx | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

(limited to 'frontend/src/components/FeedItems.tsx')

diff --git a/frontend/src/components/FeedItems.tsx b/frontend/src/components/FeedItems.tsx
index 2c3253b..ea5d8fd 100644
--- a/frontend/src/components/FeedItems.tsx
+++ b/frontend/src/components/FeedItems.tsx
@@ -19,6 +19,7 @@ export default function FeedItems() {
   const hasMoreRef = useRef(hasMore);
   const [error, setError] = useState('');
   const [selectedIndex, setSelectedIndex] = useState(-1);
+  const selectedIndexRef = useRef(selectedIndex);
 
   // Sync refs
   useEffect(() => {
@@ -33,6 +34,10 @@ export default function FeedItems() {
     hasMoreRef.current = hasMore;
   }, [hasMore]);
 
+  useEffect(() => {
+    selectedIndexRef.current = selectedIndex;
+  }, [selectedIndex]);
+
   const fetchItems = useCallback((maxId?: string) => {
     if (maxId) {
       setLoadingMore(true);
@@ -156,8 +161,9 @@ export default function FeedItems() {
       if (currentItems.length === 0) return;
 
       if (e.key === 'j') {
-        const nextIndex = Math.min(selectedIndex + 1, currentItems.length - 1);
-        if (nextIndex !== selectedIndex) {
+        const nextIndex = Math.min(selectedIndexRef.current + 1, currentItems.length - 1);
+        if (nextIndex !== selectedIndexRef.current) {
+          selectedIndexRef.current = nextIndex;
           setSelectedIndex(nextIndex);
           const item = currentItems[nextIndex];
           if (!item.read) {
@@ -174,21 +180,22 @@ export default function FeedItems() {
           fetchItems(String(currentItems[currentItems.length - 1]._id));
         }
       } else if (e.key === 'k') {
-        const nextIndex = Math.max(selectedIndex - 1, 0);
-        if (nextIndex !== selectedIndex) {
+        const nextIndex = Math.max(selectedIndexRef.current - 1, 0);
+        if (nextIndex !== selectedIndexRef.current) {
+          selectedIndexRef.current = nextIndex;
           setSelectedIndex(nextIndex);
           scrollToItem(nextIndex);
         }
       } else if (e.key === 's') {
-        if (selectedIndex >= 0 && selectedIndex < currentItems.length) {
-          toggleStar(currentItems[selectedIndex]);
+        if (selectedIndexRef.current >= 0 && selectedIndexRef.current < currentItems.length) {
+          toggleStar(currentItems[selectedIndexRef.current]);
         }
       }
     };
 
     window.addEventListener('keydown', handleKeyDown);
     return () => window.removeEventListener('keydown', handleKeyDown);
-  }, [markAsRead, scrollToItem, toggleStar, fetchItems, selectedIndex]);
+  }, [markAsRead, scrollToItem, toggleStar, fetchItems]);
 
 
   // Stable Observer
-- 
cgit v1.2.3

