diff --git a/frontend-v2/src/lib/pages/brain/AtelierBrainPage.svelte b/frontend-v2/src/lib/pages/brain/AtelierBrainPage.svelte
index aebb485..8e1703f 100644
--- a/frontend-v2/src/lib/pages/brain/AtelierBrainPage.svelte
+++ b/frontend-v2/src/lib/pages/brain/AtelierBrainPage.svelte
@@ -452,10 +452,9 @@
{/each}
- {#each sidebarTags.filter(t => t.is_active && t.item_count > 0) as tag}
+ {#each sidebarTags.filter(t => t.is_active) as tag}
{/each}
diff --git a/frontend-v2/src/lib/pages/reader/AtelierReaderPage.svelte b/frontend-v2/src/lib/pages/reader/AtelierReaderPage.svelte
index 3d7b8d1..2f097ce 100644
--- a/frontend-v2/src/lib/pages/reader/AtelierReaderPage.svelte
+++ b/frontend-v2/src/lib/pages/reader/AtelierReaderPage.svelte
@@ -27,6 +27,7 @@
let autoScrollSpeed = $state(1.5);
let articleListEl: HTMLDivElement;
let scrollRAF: number | null = null;
+ let lastScrollTs = 0;
let loading = $state(true);
let loadingMore = $state(false);
let hasMore = $state(true);
@@ -315,22 +316,31 @@
if (scrollRAF) cancelAnimationFrame(scrollRAF);
if (!usesPageScroll() && !articleListEl) return;
autoScrollActive = true;
- function step() {
+ lastScrollTs = 0;
+ function step(timestamp: number) {
if (!autoScrollActive) return;
+ const dt = lastScrollTs ? Math.min(34, timestamp - lastScrollTs) : 16;
+ lastScrollTs = timestamp;
+ const pxPerSecond = 28 * autoScrollSpeed;
+ const delta = pxPerSecond * (dt / 1000);
if (usesPageScroll()) {
- const nextY = window.scrollY + autoScrollSpeed;
- const maxY = Math.max(0, document.documentElement.scrollHeight - window.innerHeight);
- window.scrollTo({ top: Math.min(nextY, maxY), behavior: 'auto' });
- if (nextY >= maxY) {
+ const scroller = document.scrollingElement;
+ if (!scroller) return;
+ const nextY = scroller.scrollTop + delta;
+ const maxY = Math.max(0, scroller.scrollHeight - window.innerHeight);
+ scroller.scrollTop = Math.min(nextY, maxY);
+ checkScrolledCards();
+ if (nextY >= maxY - 1) {
stopAutoScroll();
return;
}
} else {
if (!articleListEl) return;
- articleListEl.scrollTop += autoScrollSpeed;
+ articleListEl.scrollTop += delta;
const maxScroll = articleListEl.scrollHeight - articleListEl.clientHeight;
- if (articleListEl.scrollTop >= maxScroll) {
+ checkScrolledCards();
+ if (articleListEl.scrollTop >= maxScroll - 1) {
stopAutoScroll();
return;
}
@@ -342,6 +352,7 @@
}
function stopAutoScroll() {
autoScrollActive = false;
+ lastScrollTs = 0;
if (scrollRAF) { cancelAnimationFrame(scrollRAF); scrollRAF = null; }
}
function toggleAutoScroll() {
@@ -366,6 +377,7 @@
let scrollCheckTimer: ReturnType | null = null;
function handleListScroll() {
+ if (usesPageScroll()) return;
// Throttle: only check every 400ms
if (scrollCheckTimer) return;
scrollCheckTimer = setTimeout(() => {
@@ -374,21 +386,40 @@
}, 400);
}
+ function handleViewportScroll() {
+ if (!usesPageScroll()) return;
+ if (scrollCheckTimer) return;
+ scrollCheckTimer = setTimeout(() => {
+ scrollCheckTimer = null;
+ checkScrolledCards();
+ }, 240);
+ }
+
function checkScrolledCards() {
if (!articleListEl) return;
- // Infinite scroll — load more when near bottom
- const { scrollTop, scrollHeight, clientHeight } = articleListEl;
- if (hasMore && !loadingMore && scrollHeight - scrollTop - clientHeight < 300) {
- loadEntries(true);
+ const cards = articleListEl.querySelectorAll('[data-entry-id]');
+ if (usesPageScroll()) {
+ const pageBottom = window.scrollY + window.innerHeight;
+ const loadThreshold = document.documentElement.scrollHeight - 500;
+ if (hasMore && !loadingMore && pageBottom >= loadThreshold) {
+ loadEntries(true);
+ }
+ } else {
+ const { scrollTop, scrollHeight, clientHeight } = articleListEl;
+ if (hasMore && !loadingMore && scrollHeight - scrollTop - clientHeight < 300) {
+ loadEntries(true);
+ }
}
- const listTop = articleListEl.getBoundingClientRect().top;
- const cards = articleListEl.querySelectorAll('[data-entry-id]');
let newlyRead = 0;
cards.forEach(card => {
- if (card.getBoundingClientRect().bottom < listTop + 20) {
+ const rect = card.getBoundingClientRect();
+ const passedThreshold = usesPageScroll()
+ ? rect.bottom < 84
+ : rect.bottom < articleListEl.getBoundingClientRect().top + 20;
+ if (passedThreshold) {
const id = Number(card.getAttribute('data-entry-id'));
if (!id) return;
const article = articles.find(a => a.id === id);
@@ -422,16 +453,17 @@
}
// ── Init ──
- onMount(() => {
+onMount(() => {
loadSidebar();
loadEntries();
return () => {
if (flushTimer) clearTimeout(flushTimer);
+ if (scrollCheckTimer) clearTimeout(scrollCheckTimer);
};
});
-
+