Logs auto scroll
This commit is contained in:
+32
-2
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { DashboardLayout } from "@/components/dashboard/DashboardLayout";
|
import { DashboardLayout } from "@/components/dashboard/DashboardLayout";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@@ -14,6 +14,10 @@ import { AnsiText } from "@/components/ansi/Ansi";
|
|||||||
|
|
||||||
export default function LogsPage() {
|
export default function LogsPage() {
|
||||||
const [isPaused, setIsPaused] = useState(false);
|
const [isPaused, setIsPaused] = useState(false);
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const bottomRef = useRef<HTMLDivElement>(null);
|
||||||
|
const shouldAutoScroll = useRef(true);
|
||||||
|
|
||||||
const [filter, setFilter] = useState<"all" | "info" | "warn" | "error">(
|
const [filter, setFilter] = useState<"all" | "info" | "warn" | "error">(
|
||||||
"all",
|
"all",
|
||||||
);
|
);
|
||||||
@@ -28,6 +32,25 @@ export default function LogsPage() {
|
|||||||
filter === "all" ? true : log.level === filter,
|
filter === "all" ? true : log.level === filter,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleScroll = () => {
|
||||||
|
const el = containerRef.current;
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
const threshold = 20; // px from bottom
|
||||||
|
const isAtBottom =
|
||||||
|
el.scrollHeight - el.scrollTop - el.clientHeight < threshold;
|
||||||
|
|
||||||
|
shouldAutoScroll.current = isAtBottom;
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!shouldAutoScroll.current || isPaused) return;
|
||||||
|
|
||||||
|
bottomRef.current?.scrollIntoView({
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
}, [filteredLogs, isPaused]);
|
||||||
|
|
||||||
const handleExport = () => {
|
const handleExport = () => {
|
||||||
// Convert data to JSON string
|
// Convert data to JSON string
|
||||||
const json = JSON.stringify(filteredLogs, null, 2);
|
const json = JSON.stringify(filteredLogs, null, 2);
|
||||||
@@ -156,7 +179,11 @@ export default function LogsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|
||||||
<CardContent className="p-0 overflow-auto h-[calc(100%-48px)]">
|
<CardContent
|
||||||
|
className="p-0 overflow-auto h-[calc(100%-48px)]"
|
||||||
|
ref={containerRef}
|
||||||
|
onScroll={handleScroll}
|
||||||
|
>
|
||||||
<div className="font-mono text-xs px-3 md:px-6 space-y-2 md:space-y-0">
|
<div className="font-mono text-xs px-3 md:px-6 space-y-2 md:space-y-0">
|
||||||
{filteredLogs.map((log, index) => (
|
{filteredLogs.map((log, index) => (
|
||||||
<div
|
<div
|
||||||
@@ -188,6 +215,9 @@ export default function LogsPage() {
|
|||||||
{log.message}
|
{log.message}
|
||||||
</span> */}
|
</span> */}
|
||||||
<AnsiText text={log.message} />
|
<AnsiText text={log.message} />
|
||||||
|
|
||||||
|
{/* Scroll anchor */}
|
||||||
|
<div ref={bottomRef} />
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user