From 1362f00d39e90aef8a8338887222541a5a992178 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 18 Feb 2026 21:52:21 +0000 Subject: 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 --- frontend-vanilla/src/main.ts | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'frontend-vanilla/src/main.ts') 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. -- cgit v1.2.3 From 0876a683cdc344b200dbd65aa137969a1528c85d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 18 Feb 2026 22:56:56 +0000 Subject: Revert polling interval back to 1s https://claude.ai/code/session_0187FXrbScDSWfbNEk9SfJaj --- frontend-vanilla/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'frontend-vanilla/src/main.ts') diff --git a/frontend-vanilla/src/main.ts b/frontend-vanilla/src/main.ts index 75707ac..e3d3a71 100644 --- a/frontend-vanilla/src/main.ts +++ b/frontend-vanilla/src/main.ts @@ -397,7 +397,7 @@ if (typeof window !== 'undefined') { } } - }, 3000); + }, 1000); } // ... (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. -- cgit v1.2.3