diff --git a/decnet_web/src/components/Dashboard.css b/decnet_web/src/components/Dashboard.css
index 0f0546cf..92c418bf 100644
--- a/decnet_web/src/components/Dashboard.css
+++ b/decnet_web/src/components/Dashboard.css
@@ -5,6 +5,12 @@
height: 100%;
}
+/* Detail/scroll pages: grow with content instead of locking to viewport */
+.dashboard.page-scroll {
+ height: auto;
+ min-height: 0;
+}
+
/* Page header */
.page-header {
display: flex;
@@ -275,6 +281,7 @@
flex: 1;
overflow-y: auto;
min-height: 0;
+ max-height: none;
}
.dash-side {
diff --git a/decnet_web/src/components/Identities.tsx b/decnet_web/src/components/Identities.tsx
index 69a6f716..b6c5b230 100644
--- a/decnet_web/src/components/Identities.tsx
+++ b/decnet_web/src/components/Identities.tsx
@@ -8,6 +8,7 @@ import EmptyState from './EmptyState/EmptyState';
import { useFocusSearch } from '../hooks/useFocusSearch';
import { useIdentityStream } from './useIdentityStream';
import './Dashboard.css';
+import './Attackers.css';
interface IdentityEntry {
uuid: string;
diff --git a/decnet_web/src/components/Layout.css b/decnet_web/src/components/Layout.css
index a3146e22..0cc87414 100644
--- a/decnet_web/src/components/Layout.css
+++ b/decnet_web/src/components/Layout.css
@@ -348,6 +348,6 @@
}
/* Dashboard fills the viewport without scrolling — panels scroll internally */
-.content-viewport:has(> .dashboard) {
+.content-viewport:has(> .dashboard > .dash-grid) {
overflow: hidden;
}
diff --git a/decnet_web/src/components/Layout.tsx b/decnet_web/src/components/Layout.tsx
index 64517ad2..66e5683e 100644
--- a/decnet_web/src/components/Layout.tsx
+++ b/decnet_web/src/components/Layout.tsx
@@ -66,7 +66,9 @@ const Layout: React.FC
= ({
alertCount: alertCountProp = 0,
build = 'v0.1',
}) => {
- const [sidebarOpen, setSidebarOpen] = useState(true);
+ const [sidebarOpen, setSidebarOpen] = useState(() => {
+ try { return localStorage.getItem('decnet_sidebar_open') !== 'false'; } catch { return true; }
+ });
const [search, setSearch] = useState('');
const [systemActive, setSystemActive] = useState(false);
const [clockTime, setClockTime] = useState(() => formatClock(new Date()));
@@ -106,7 +108,11 @@ const Layout: React.FC = ({
{sidebarOpen &&
DECNET}
-
@@ -255,13 +261,21 @@ interface NavGroupProps {
}
const NavGroup: React.FC = ({ label, icon, open, children }) => {
- const [expanded, setExpanded] = useState(true);
+ const storageKey = `decnet_navgroup_${label.toLowerCase()}`;
+ const [expanded, setExpanded] = useState(() => {
+ try { return localStorage.getItem(storageKey) === 'true'; } catch { return false; }
+ });
+ const toggle = () => setExpanded(v => {
+ const next = !v;
+ try { localStorage.setItem(storageKey, String(next)); } catch {}
+ return next;
+ });
return (
setExpanded((v) => !v)}
+ onClick={toggle}
>
{icon}
{open && (
diff --git a/decnet_web/src/components/LiveLogs.tsx b/decnet_web/src/components/LiveLogs.tsx
index 18614d26..34925387 100644
--- a/decnet_web/src/components/LiveLogs.tsx
+++ b/decnet_web/src/components/LiveLogs.tsx
@@ -274,17 +274,15 @@ const LiveLogs: React.FC = () => {
SHOWING {filteredLogs.length} OF {totalLogs.toLocaleString()}
- {!streaming && (
-
- Page {page} of {totalPages}
- changePage(page - 1)} aria-label="Previous page">
-
-
- = totalPages} onClick={() => changePage(page + 1)} aria-label="Next page">
-
-
-
- )}
+
+ Page {page} of {totalPages}
+ changePage(page - 1)} aria-label="Previous page">
+
+
+ = totalPages} onClick={() => changePage(page + 1)} aria-label="Next page">
+
+
+