diff --git a/client/src/components/History/HistoryScrollList.vue b/client/src/components/History/HistoryScrollList.vue
index 55d3e4f8da59..a2a69921fb09 100644
--- a/client/src/components/History/HistoryScrollList.vue
+++ b/client/src/components/History/HistoryScrollList.vue
@@ -10,6 +10,8 @@ import { computed, onMounted, onUnmounted, type PropType, type Ref, ref, watch }
import { useRouter } from "vue-router/composables";
import type { HistoryDetailed, HistorySummary } from "@/api";
+import { useAnimationFrameResizeObserver } from "@/composables/sensors/animationFrameResizeObserver";
+import { useAnimationFrameScroll } from "@/composables/sensors/animationFrameScroll";
import { useHistoryStore } from "@/stores/historyStore";
import { useUserStore } from "@/stores/userStore";
import localize from "@/utils/localization";
@@ -55,6 +57,15 @@ const hasNoResults = computed(() => props.filter && filtered.value.length == 0);
const validFilter = computed(() => props.filter && props.filter.length > 2);
const allLoaded = computed(() => totalHistoryCount.value <= filtered.value.length);
+// check if we have scrolled to the top or bottom of the scrollable div
+const { arrived } = useAnimationFrameScroll(scrollableDiv);
+const isScrollable = ref(false);
+useAnimationFrameResizeObserver(scrollableDiv, ({ clientSize, scrollSize }) => {
+ isScrollable.value = scrollSize.height >= clientSize.height + 1;
+});
+const scrolledTop = computed(() => !isScrollable.value || arrived.top);
+const scrolledBottom = computed(() => !isScrollable.value || arrived.bottom);
+
onMounted(async () => {
useInfiniteScroll(scrollableDiv.value, () => loadMore());
// if mounted with a filter, load histories for filter
@@ -190,7 +201,13 @@ async function loadMore(noScroll = false) {