aboutsummaryrefslogtreecommitdiffstats
path: root/frontend-vanilla/src/main.ts
diff options
context:
space:
mode:
authorClaude <noreply@anthropic.com>2026-02-18 21:52:21 +0000
committerClaude <noreply@anthropic.com>2026-02-18 21:52:21 +0000
commit1362f00d39e90aef8a8338887222541a5a992178 (patch)
tree11a98c935af0b3da96580425c2ca71ea5686e356 /frontend-vanilla/src/main.ts
parent7776e81b39130c211eb0ec566c6467a28a9fa64c (diff)
downloadneko-1362f00d39e90aef8a8338887222541a5a992178.tar.gz
neko-1362f00d39e90aef8a8338887222541a5a992178.tar.bz2
neko-1362f00d39e90aef8a8338887222541a5a992178.zip
Fix theme performance regressions affecting mobile scroll
The new theme CSS files introduced several patterns that cause scroll jank and memory pressure, especially on mobile: - terminal.css: Full-viewport fixed pseudo-element with repeating gradient scanlines forced GPU compositing on every scroll frame. Now limited to desktop only with will-change layer promotion. - codex.css/sakura.css: text-rendering: optimizeLegibility on body triggered expensive kerning/ligature computation on all text. - codex.css: font-feature-settings forced text shaper on every glyph. - codex.css: hyphens: auto required dictionary lookups during layout. - style.css: transition: all on buttons and sidebar links caused unnecessary animation work during scroll hover state changes. - main.ts: checkReadItems did O(n) individual querySelector calls per scroll tick; switched to single querySelectorAll batch query. - Polling interval reduced from 1s to 3s (scroll handler already covers the normal case, polling is just a robustness fallback). https://claude.ai/code/session_0187FXrbScDSWfbNEk9SfJaj
Diffstat (limited to 'frontend-vanilla/src/main.ts')
-rw-r--r--frontend-vanilla/src/main.ts26
1 files changed, 14 insertions, 12 deletions
diff --git a/frontend-vanilla/src/main.ts b/frontend-vanilla/src/main.ts
index 2be9354..75707ac 100644
--- a/frontend-vanilla/src/main.ts
+++ b/frontend-vanilla/src/main.ts
@@ -340,18 +340,20 @@ export function renderItems() {
function checkReadItems(scrollRoot: HTMLElement) {
const containerRect = scrollRoot.getBoundingClientRect();
- store.items.forEach((item) => {
- if (item.read) return;
-
- const el = document.querySelector(`.feed-item[data-id="${item._id}"]`);
- if (el) {
- const rect = el.getBoundingClientRect();
- // Mark as read if the bottom of the item is above the top of the container
- if (rect.bottom < containerRect.top) {
- updateItem(item._id, { read: true });
- }
+ // Batch DOM query: select all feed items at once instead of O(n) individual
+ // querySelector calls with attribute selectors per scroll tick.
+ const allItems = scrollRoot.querySelectorAll('.feed-item');
+ for (const el of allItems) {
+ const id = parseInt(el.getAttribute('data-id')!);
+ const item = store.items.find(i => i._id === id);
+ if (!item || item.read) continue;
+
+ const rect = el.getBoundingClientRect();
+ // Mark as read if the bottom of the item is above the top of the container
+ if (rect.bottom < containerRect.top) {
+ updateItem(item._id, { read: true });
}
- });
+ }
}
// Polling fallback for infinite scroll (matches V1 behavior)
@@ -395,7 +397,7 @@ if (typeof window !== 'undefined') {
}
}
- }, 1000);
+ }, 3000);
}
// ... (add this variable at module level or inside renderSettings if possible, but module level is safer for persistence across clicks if renderSettings re-runs? No, event flow is synchronous: click button -> click file input. User selects file. Change event fires.