Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0b1cbdc5b | ||
|
|
d101d883a9 |
@@ -81,25 +81,25 @@ export function EventFeed({ events, maxItems = 200, className }: EventFeedProps)
|
|||||||
.filter((e) => activeFilters.size === 0 || activeFilters.has(e.severity))
|
.filter((e) => activeFilters.size === 0 || activeFilters.has(e.severity))
|
||||||
.filter((e) => !searchLower || getSearchableText(e).toLowerCase().includes(searchLower))
|
.filter((e) => !searchLower || getSearchableText(e).toLowerCase().includes(searchLower))
|
||||||
|
|
||||||
// Auto-scroll to bottom
|
// Auto-scroll to top (newest entries are at top in desc sort)
|
||||||
const scrollToBottom = useCallback(() => {
|
const scrollToTop = useCallback(() => {
|
||||||
const el = scrollRef.current
|
const el = scrollRef.current
|
||||||
if (el) {
|
if (el) {
|
||||||
el.scrollTop = el.scrollHeight
|
el.scrollTop = 0
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isPaused) {
|
if (!isPaused) {
|
||||||
scrollToBottom()
|
scrollToTop()
|
||||||
}
|
}
|
||||||
}, [events, isPaused, scrollToBottom])
|
}, [events, isPaused, scrollToTop])
|
||||||
|
|
||||||
function handleScroll() {
|
function handleScroll() {
|
||||||
const el = scrollRef.current
|
const el = scrollRef.current
|
||||||
if (!el) return
|
if (!el) return
|
||||||
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < 8
|
const atTop = el.scrollTop < 8
|
||||||
setIsPaused(!atBottom)
|
setIsPaused(!atTop)
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleFilter(severity: SeverityFilter) {
|
function toggleFilter(severity: SeverityFilter) {
|
||||||
@@ -196,10 +196,10 @@ export function EventFeed({ events, maxItems = 200, className }: EventFeedProps)
|
|||||||
className={styles.resumeBtn}
|
className={styles.resumeBtn}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsPaused(false)
|
setIsPaused(false)
|
||||||
scrollToBottom()
|
scrollToTop()
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
↓ Resume auto-scroll
|
↑ Scroll to latest
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
.container {
|
.container {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
background: var(--bg-inset);
|
|
||||||
border-radius: var(--radius-md);
|
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,18 +35,18 @@ function formatTime(iso: string): string {
|
|||||||
|
|
||||||
export function LogViewer({ entries, maxHeight = 400, className }: LogViewerProps) {
|
export function LogViewer({ entries, maxHeight = 400, className }: LogViewerProps) {
|
||||||
const scrollRef = useRef<HTMLDivElement>(null)
|
const scrollRef = useRef<HTMLDivElement>(null)
|
||||||
const isAtBottomRef = useRef(true)
|
const isAtTopRef = useRef(true)
|
||||||
|
|
||||||
const handleScroll = useCallback(() => {
|
const handleScroll = useCallback(() => {
|
||||||
const el = scrollRef.current
|
const el = scrollRef.current
|
||||||
if (!el) return
|
if (!el) return
|
||||||
isAtBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 20
|
isAtTopRef.current = el.scrollTop < 20
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const el = scrollRef.current
|
const el = scrollRef.current
|
||||||
if (el && isAtBottomRef.current) {
|
if (el && isAtTopRef.current) {
|
||||||
el.scrollTop = el.scrollHeight
|
el.scrollTop = 0
|
||||||
}
|
}
|
||||||
}, [entries])
|
}, [entries])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user