Update UI for Sitemap to show last 20 and scroll to button.
Build Docker Image / docker (push) Successful in 6s

This commit is contained in:
2026-04-09 11:33:18 -07:00
parent b166c03e9e
commit 8600ee9c27
+46 -9
View File
@@ -2,15 +2,18 @@ from __future__ import annotations
import contextlib
import csv
import html
import importlib.util
import io
import os
import re
import sys
import time
from collections import deque
from pathlib import Path
import streamlit as st
import streamlit.components.v1 as components
ROOT_DIR = Path(__file__).resolve().parent
@@ -58,17 +61,26 @@ def read_csv_preview(csv_bytes: bytes, limit: int = 200) -> list[dict[str, str]]
class StreamlitOutputBuffer(io.TextIOBase):
def __init__(self, placeholder, *, height: int = 220, throttle_seconds: float = 0.2) -> None:
def __init__(self, placeholder, *, height: int = 220, throttle_seconds: float = 0.2, max_lines: int = 20) -> None:
self.placeholder = placeholder
self.height = height
self.throttle_seconds = throttle_seconds
self.parts: list[str] = []
self.max_lines = max_lines
self.lines: deque[str] = deque(maxlen=max_lines)
self.current_line = ""
self.last_render = 0.0
def write(self, text: str) -> int:
if not text:
return 0
self.parts.append(text)
normalized = text.replace("\r\n", "\n").replace("\r", "\n")
for chunk in normalized.splitlines(keepends=True):
if chunk.endswith("\n"):
self.current_line += chunk[:-1]
self.lines.append(self.current_line)
self.current_line = ""
else:
self.current_line += chunk
now = time.monotonic()
if "\n" in text or (now - self.last_render) >= self.throttle_seconds:
self.render()
@@ -79,15 +91,40 @@ class StreamlitOutputBuffer(io.TextIOBase):
def render(self) -> None:
self.last_render = time.monotonic()
self.placeholder.text_area(
"Scan Details",
value="".join(self.parts),
height=self.height,
disabled=True,
visible_lines = list(self.lines)
if self.current_line:
visible_lines.append(self.current_line)
visible_lines = visible_lines[-self.max_lines :]
content = html.escape("\n".join(visible_lines))
self.placeholder.caption("Scan Details")
components.html(
f"""
<div id="scan-details" style="
height: {self.height}px;
overflow-y: auto;
white-space: pre-wrap;
font-family: monospace;
font-size: 0.9rem;
padding: 0.75rem;
border: 1px solid rgba(49, 51, 63, 0.2);
border-radius: 0.5rem;
background: white;
">{content}</div>
<script>
const el = document.getElementById("scan-details");
if (el) {{
el.scrollTop = el.scrollHeight;
}}
</script>
""",
height=self.height + 16,
)
def getvalue(self) -> str:
return "".join(self.parts)
visible_lines = list(self.lines)
if self.current_line:
visible_lines.append(self.current_line)
return "\n".join(visible_lines[-self.max_lines :])
def render_sitemap_tab() -> None: