diff --git a/ui/src/components/IssuesList.tsx b/ui/src/components/IssuesList.tsx
index 498752b4..e9f7ac8d 100644
--- a/ui/src/components/IssuesList.tsx
+++ b/ui/src/components/IssuesList.tsx
@@ -18,7 +18,7 @@ import { Input } from "@/components/ui/input";
import { Popover, PopoverTrigger, PopoverContent } from "@/components/ui/popover";
import { Checkbox } from "@/components/ui/checkbox";
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "@/components/ui/collapsible";
-import { CircleDot, Plus, Filter, ArrowUpDown, Layers, Check, X, ChevronRight, List, Columns3, User, Search, ArrowDown } from "lucide-react";
+import { CircleDot, Plus, Filter, ArrowUpDown, Layers, Check, X, ChevronRight, List, Columns3, User, Search } from "lucide-react";
import { KanbanBoard } from "./KanbanBoard";
import type { Issue } from "@paperclipai/shared";
@@ -234,24 +234,6 @@ export function IssuesList({
const activeFilterCount = countActiveFilters(viewState);
- const [showScrollBottom, setShowScrollBottom] = useState(false);
- useEffect(() => {
- const el = document.getElementById("main-content");
- if (!el) return;
- const check = () => {
- const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
- setShowScrollBottom(distanceFromBottom > 300);
- };
- check();
- el.addEventListener("scroll", check, { passive: true });
- return () => el.removeEventListener("scroll", check);
- }, [filtered.length]);
-
- const scrollToBottom = useCallback(() => {
- const el = document.getElementById("main-content");
- if (el) el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
- }, []);
-
const groupedContent = useMemo(() => {
if (viewState.groupBy === "none") {
return [{ key: "__all", label: null as string | null, items: filtered }];
@@ -755,15 +737,6 @@ export function IssuesList({
))
)}
- {showScrollBottom && (
-
- )}
);
}
diff --git a/ui/src/components/ScrollToBottom.tsx b/ui/src/components/ScrollToBottom.tsx
new file mode 100644
index 00000000..4ea8a494
--- /dev/null
+++ b/ui/src/components/ScrollToBottom.tsx
@@ -0,0 +1,40 @@
+import { useCallback, useEffect, useState } from "react";
+import { ArrowDown } from "lucide-react";
+
+/**
+ * Floating scroll-to-bottom button that appears when the user is far from the
+ * bottom of the `#main-content` scroll container. Hides when within 300px of
+ * the bottom. Positioned to avoid the mobile bottom nav.
+ */
+export function ScrollToBottom() {
+ const [visible, setVisible] = useState(false);
+
+ useEffect(() => {
+ const el = document.getElementById("main-content");
+ if (!el) return;
+ const check = () => {
+ const distance = el.scrollHeight - el.scrollTop - el.clientHeight;
+ setVisible(distance > 300);
+ };
+ check();
+ el.addEventListener("scroll", check, { passive: true });
+ return () => el.removeEventListener("scroll", check);
+ }, []);
+
+ const scroll = useCallback(() => {
+ const el = document.getElementById("main-content");
+ if (el) el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
+ }, []);
+
+ if (!visible) return null;
+
+ return (
+
+ );
+}
diff --git a/ui/src/pages/AgentDetail.tsx b/ui/src/pages/AgentDetail.tsx
index 06c3a2f4..596cb98d 100644
--- a/ui/src/pages/AgentDetail.tsx
+++ b/ui/src/pages/AgentDetail.tsx
@@ -24,6 +24,7 @@ import { CopyText } from "../components/CopyText";
import { EntityRow } from "../components/EntityRow";
import { Identity } from "../components/Identity";
import { PageSkeleton } from "../components/PageSkeleton";
+import { ScrollToBottom } from "../components/ScrollToBottom";
import { formatCents, formatDate, relativeTime, formatTokens } from "../lib/utils";
import { cn } from "../lib/utils";
import { Button } from "@/components/ui/button";
@@ -1747,6 +1748,7 @@ function RunDetail({ run, agentRouteId, adapterType }: { run: HeartbeatRun; agen
{/* Log viewer */}
+
);
}
diff --git a/ui/src/pages/IssueDetail.tsx b/ui/src/pages/IssueDetail.tsx
index 90c94888..a0266c16 100644
--- a/ui/src/pages/IssueDetail.tsx
+++ b/ui/src/pages/IssueDetail.tsx
@@ -18,6 +18,7 @@ import { CommentThread } from "../components/CommentThread";
import { IssueProperties } from "../components/IssueProperties";
import { LiveRunWidget } from "../components/LiveRunWidget";
import type { MentionOption } from "../components/MarkdownEditor";
+import { ScrollToBottom } from "../components/ScrollToBottom";
import { StatusIcon } from "../components/StatusIcon";
import { PriorityIcon } from "../components/PriorityIcon";
import { StatusBadge } from "../components/StatusBadge";
@@ -926,6 +927,7 @@ export function IssueDetail() {
+
);
}