Update UI for Sitemap to show last 20 and scroll to button.
Build Docker Image / docker (push) Successful in 6s
Build Docker Image / docker (push) Successful in 6s
This commit is contained in:
@@ -2,15 +2,18 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import csv
|
import csv
|
||||||
|
import html
|
||||||
import importlib.util
|
import importlib.util
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
from collections import deque
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
import streamlit.components.v1 as components
|
||||||
|
|
||||||
|
|
||||||
ROOT_DIR = Path(__file__).resolve().parent
|
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):
|
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.placeholder = placeholder
|
||||||
self.height = height
|
self.height = height
|
||||||
self.throttle_seconds = throttle_seconds
|
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
|
self.last_render = 0.0
|
||||||
|
|
||||||
def write(self, text: str) -> int:
|
def write(self, text: str) -> int:
|
||||||
if not text:
|
if not text:
|
||||||
return 0
|
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()
|
now = time.monotonic()
|
||||||
if "\n" in text or (now - self.last_render) >= self.throttle_seconds:
|
if "\n" in text or (now - self.last_render) >= self.throttle_seconds:
|
||||||
self.render()
|
self.render()
|
||||||
@@ -79,15 +91,40 @@ class StreamlitOutputBuffer(io.TextIOBase):
|
|||||||
|
|
||||||
def render(self) -> None:
|
def render(self) -> None:
|
||||||
self.last_render = time.monotonic()
|
self.last_render = time.monotonic()
|
||||||
self.placeholder.text_area(
|
visible_lines = list(self.lines)
|
||||||
"Scan Details",
|
if self.current_line:
|
||||||
value="".join(self.parts),
|
visible_lines.append(self.current_line)
|
||||||
height=self.height,
|
visible_lines = visible_lines[-self.max_lines :]
|
||||||
disabled=True,
|
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:
|
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:
|
def render_sitemap_tab() -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user