From 96f441de40b18144f4db60bdcfba6fe9eea24335 Mon Sep 17 00:00:00 2001 From: henrygd Date: Fri, 29 Aug 2025 16:16:45 -0400 Subject: [PATCH] Virtualize All Systems table to improve performance with hundreds of systems (#1100) - Also truncate long system names in tables and alerts sheet. (#1104) --- beszel/site/bun.lockb | Bin 224015 -> 224846 bytes beszel/site/package.json | 1 + beszel/site/src/components/add-system.tsx | 2 +- .../src/components/alerts-history-columns.tsx | 4 +- .../src/components/alerts/alerts-sheet.tsx | 2 +- beszel/site/src/components/routes/home.tsx | 2 +- .../settings/alerts-history-data-table.tsx | 4 +- .../src/components/routes/settings/layout.tsx | 2 +- .../routes/settings/tokens-fingerprints.tsx | 8 +- beszel/site/src/components/routes/system.tsx | 2 +- .../systems-table/systems-table-columns.tsx | 7 +- .../systems-table/systems-table.tsx | 173 +++++++++++------- beszel/site/src/components/ui/command.tsx | 2 +- beszel/site/src/components/ui/table.tsx | 6 +- beszel/site/src/index.css | 8 +- beszel/site/src/lib/api.ts | 22 ++- beszel/site/src/lib/stores.ts | 5 + beszel/site/src/main.tsx | 2 +- .../systems-table/systems-table.tsx | 93 ---------- supplemental/CHANGELOG.md | 6 + 20 files changed, 176 insertions(+), 175 deletions(-) delete mode 100644 site/src/components/systems-table/systems-table.tsx diff --git a/beszel/site/bun.lockb b/beszel/site/bun.lockb index 773db0ab8e3d4d2a1067d57c28efaddcb1034638..98e3b25897c8161d9e7226697bf8b0735e2052c1 100755 GIT binary patch delta 39532 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 delta 38735 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 > - + {system ? `${t`Edit`} ${system?.name}` : Add New System} diff --git a/beszel/site/src/components/alerts-history-columns.tsx b/beszel/site/src/components/alerts-history-columns.tsx index 4bddce7..390bd5c 100644 --- a/beszel/site/src/components/alerts-history-columns.tsx +++ b/beszel/site/src/components/alerts-history-columns.tsx @@ -16,7 +16,9 @@ export const alertsHistoryColumns: ColumnDef[] = [ System ), - cell: ({ row }) => {row.original.expand?.system?.name || row.original.system}, + cell: ({ row }) => ( +
{row.original.expand?.system?.name || row.original.system}
+ ), filterFn: (row, _, filterValue) => { const display = row.original.expand?.system?.name || row.original.system || "" return display.toLowerCase().includes(filterValue.toLowerCase()) diff --git a/beszel/site/src/components/alerts/alerts-sheet.tsx b/beszel/site/src/components/alerts/alerts-sheet.tsx index 24539a7..561985c 100644 --- a/beszel/site/src/components/alerts/alerts-sheet.tsx +++ b/beszel/site/src/components/alerts/alerts-sheet.tsx @@ -96,7 +96,7 @@ export const AlertDialogContent = memo(function AlertDialogContent({ system }: { - {system.name} + {system.name} diff --git a/beszel/site/src/components/routes/home.tsx b/beszel/site/src/components/routes/home.tsx index 12e5ec2..7a540b2 100644 --- a/beszel/site/src/components/routes/home.tsx +++ b/beszel/site/src/components/routes/home.tsx @@ -44,7 +44,7 @@ export default memo(function () { -
+
{table.getHeaderGroups().map((headerGroup) => ( - + {headerGroup.headers.map((header) => ( {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} ))} - + ))} diff --git a/beszel/site/src/components/routes/settings/layout.tsx b/beszel/site/src/components/routes/settings/layout.tsx index e7e757e..d64ce6b 100644 --- a/beszel/site/src/components/routes/settings/layout.tsx +++ b/beszel/site/src/components/routes/settings/layout.tsx @@ -102,7 +102,7 @@ export default function SettingsLayout() { }, []) return ( - + Settings diff --git a/beszel/site/src/components/routes/settings/tokens-fingerprints.tsx b/beszel/site/src/components/routes/settings/tokens-fingerprints.tsx index d5d88b9..d734ac9 100644 --- a/beszel/site/src/components/routes/settings/tokens-fingerprints.tsx +++ b/beszel/site/src/components/routes/settings/tokens-fingerprints.tsx @@ -272,7 +272,7 @@ const SectionTable = memo(({ fingerprints = [] }: { fingerprints: FingerprintRec
- + {headerCols.map((col) => ( @@ -288,12 +288,14 @@ const SectionTable = memo(({ fingerprints = [] }: { fingerprints: FingerprintRec )} - + {fingerprints.map((fingerprint, i) => ( - {fingerprint.expand.system.name} + + {fingerprint.expand.system.name} + {fingerprint.token} {fingerprint.fingerprint} {!isReadOnly && ( diff --git a/beszel/site/src/components/routes/system.tsx b/beszel/site/src/components/routes/system.tsx index cef2f14..65100a5 100644 --- a/beszel/site/src/components/routes/system.tsx +++ b/beszel/site/src/components/routes/system.tsx @@ -391,7 +391,7 @@ export default function SystemDetail({ name }: { name: string }) { return ( <> -
+
{/* system info */}
diff --git a/beszel/site/src/components/systems-table/systems-table-columns.tsx b/beszel/site/src/components/systems-table/systems-table-columns.tsx index 27746a4..20864a7 100644 --- a/beszel/site/src/components/systems-table/systems-table-columns.tsx +++ b/beszel/site/src/components/systems-table/systems-table-columns.tsx @@ -27,7 +27,7 @@ import { } from "@/lib/utils" import { EthernetIcon, GpuIcon, HourglassIcon, ThermometerIcon } from "../ui/icons" import { useStore } from "@nanostores/react" -import { $userSettings } from "@/lib/stores" +import { $longestSystemNameLen, $userSettings } from "@/lib/stores" import { Trans, useLingui } from "@lingui/react/macro" import { useMemo, useRef, useState } from "react" import { memo } from "react" @@ -111,11 +111,14 @@ export default function SystemsTableColumns(viewMode: "table" | "grid"): ColumnD Icon: ServerIcon, cell: (info) => { const { name } = info.row.original + const longestName = useStore($longestSystemNameLen) return ( <> - {name} + + {name} + {viewMode === "table" ? ( // table layout -
+
) : ( @@ -277,36 +278,78 @@ export default function SystemsTable() { ) } -const AllSystemsTable = memo( - ({ table, rows, colLength }: { table: TableType; rows: Row[]; colLength: number }) => { - return ( -
- - - {rows.length ? ( - rows.map((row) => ( - - )) - ) : ( - - - No systems found. - - - )} - -
- ) - } -) +const AllSystemsTable = memo(function ({ + table, + rows, + colLength, +}: { + table: TableType + rows: Row[] + colLength: number +}) { + // The virtualizer will need a reference to the scrollable container element + const scrollRef = useRef(null) + + const virtualizer = useVirtualizer({ + count: rows.length, + estimateSize: () => (rows.length > 10 ? 56 : 60), + getScrollElement: () => scrollRef.current, + overscan: 5, + }) + const virtualRows = virtualizer.getVirtualItems() + + const paddingTop = Math.max(0, virtualRows[0]?.start ?? 0 - virtualizer.options.scrollMargin) + const paddingBottom = Math.max(0, virtualizer.getTotalSize() - (virtualRows[virtualRows.length - 1]?.end ?? 0)) + + return ( +
2) && "min-h-50" + )} + ref={scrollRef} + > + {/* add header height to table size */} +
+ + + + {rows.length ? ( + virtualRows.map((virtualRow) => { + const row = rows[virtualRow.index] as Row + return ( + + ) + }) + ) : ( + + + No systems found. + + + )} + +
+
+
+ ) +}) function SystemsTableHead({ table, colLength }: { table: TableType; colLength: number }) { const { i18n } = useLingui() + return useMemo(() => { return ( - + {table.getHeaderGroups().map((headerGroup) => ( - + {headerGroup.headers.map((header) => { return ( @@ -314,41 +357,49 @@ function SystemsTableHead({ table, colLength }: { table: TableType ) })} - + ))} ) }, [i18n.locale, colLength]) } -const SystemTableRow = memo( - ({ row, length, colLength }: { row: Row; length: number; colLength: number }) => { - const system = row.original - const { t } = useLingui() - return useMemo(() => { - return ( - - {row.getVisibleCells().map((cell) => ( - 10 ? "py-2" : "py-2.5"} - > - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} - - ) - }, [system, system.status, colLength, t]) - } -) +const SystemTableRow = memo(function ({ + row, + virtualRow, + colLength, +}: { + row: Row + virtualRow: VirtualItem + length: number + colLength: number +}) { + const system = row.original + const { t } = useLingui() + return useMemo(() => { + return ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + ) + }, [system, system.status, colLength, t]) +}) const SystemCard = memo( ({ row, table, colLength }: { row: Row; table: TableType; colLength: number }) => { @@ -368,13 +419,11 @@ const SystemCard = memo( )} > -
- -
+
+ +
- - {system.name} - + {system.name}
{table.getColumn("actions")?.getIsVisible() && ( diff --git a/beszel/site/src/components/ui/command.tsx b/beszel/site/src/components/ui/command.tsx index 4b5c22f..31f8f92 100644 --- a/beszel/site/src/components/ui/command.tsx +++ b/beszel/site/src/components/ui/command.tsx @@ -101,7 +101,7 @@ function CommandItem({ className, ...props }: React.ComponentProps>( ({ className, ...props }, ref) => ( - + ) ) TableHeader.displayName = "TableHeader" diff --git a/beszel/site/src/index.css b/beszel/site/src/index.css index 5b62e44..fa63fd4 100644 --- a/beszel/site/src/index.css +++ b/beszel/site/src/index.css @@ -1,14 +1,15 @@ @import "tailwindcss"; @import "tw-animate-css"; +@custom-variant light (&:is(.light *)); @custom-variant dark (&:is(.dark *)); @custom-variant safari (@supports (hanging-punctuation: first) and (-webkit-appearance: none)); :root { --background: hsl(30 8% 98%); - --foreground: hsl(30 0% 0%); + --foreground: hsl(30 0% 10%); --card: hsl(30 0% 100%); - --card-foreground: hsl(240 6.67% 2.94%); + --card-foreground: hsl(240 6% 12%); --popover: hsl(30 0% 100%); --popover-foreground: hsl(240 10% 6.2%); --primary: hsl(240 5.88% 10%); @@ -30,6 +31,7 @@ --chart-3: hsl(30 80% 55%); --chart-4: hsl(280 65% 60%); --chart-5: hsl(340 75% 55%); + --table-header: hsl(225, 6%, 97%); } .dark { @@ -53,6 +55,7 @@ --border: hsl(220 3% 16%); --input: hsl(220 4% 22%); --ring: hsl(220 4% 80%); + --table-header: hsl(220, 6%, 13%); --radius: 0.8rem; } @@ -103,6 +106,7 @@ --color-chart-3: var(--chart-3); --color-chart-4: var(--chart-4); --color-chart-5: var(--chart-5); + --color-table-header: var(--table-header); } @layer utilities { diff --git a/beszel/site/src/lib/api.ts b/beszel/site/src/lib/api.ts index 1d16ee7..6ae6a44 100644 --- a/beszel/site/src/lib/api.ts +++ b/beszel/site/src/lib/api.ts @@ -1,5 +1,5 @@ import { ChartTimes, SystemRecord, UserSettings } from "@/types" -import { $alerts, $systems, $userSettings } from "./stores" +import { $alerts, $longestSystemNameLen, $systems, $userSettings } from "./stores" import { toast } from "@/components/ui/use-toast" import { t } from "@lingui/core/macro" import { chartTimeData } from "./utils" @@ -88,11 +88,29 @@ export const updateSystemList = (() => { } isFetchingSystems = true try { - const records = await pb + let records = await pb .collection("systems") .getFullList({ sort: "+name", fields: "id,name,host,port,info,status" }) if (records.length) { + // records = [ + // ...records, + // ...records, + // ...records, + // ...records, + // ...records, + // ...records, + // ...records, + // ...records, + // ...records, + // ] + // we need to loop once to get the longest name + let longestName = $longestSystemNameLen.get() + for (const { name } of records) { + if (name.length > longestName) { + $longestSystemNameLen.set(Math.min(20, name.length)) + } + } $systems.set(records) } else { verifyAuth() diff --git a/beszel/site/src/lib/stores.ts b/beszel/site/src/lib/stores.ts index 9a0ed48..f76b6db 100644 --- a/beszel/site/src/lib/stores.ts +++ b/beszel/site/src/lib/stores.ts @@ -53,3 +53,8 @@ export const $copyContent = atom("") /** Direction for localization */ export const $direction = atom<"ltr" | "rtl">("ltr") + +/** Longest system name length. Used to set table column width. I know this + * is stupid but the table is virtualized and I know this will work. + */ +export const $longestSystemNameLen = atom(8) diff --git a/beszel/site/src/main.tsx b/beszel/site/src/main.tsx index 56a0c10..eaf0ea7 100644 --- a/beszel/site/src/main.tsx +++ b/beszel/site/src/main.tsx @@ -102,7 +102,7 @@ const Layout = () => {
-
+
{copyContent && ( diff --git a/site/src/components/systems-table/systems-table.tsx b/site/src/components/systems-table/systems-table.tsx deleted file mode 100644 index 57c177e..0000000 --- a/site/src/components/systems-table/systems-table.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { memo, useMemo } from "react" -import { Row, TableType } from "@tanstack/react-table" -import { useLingui } from "@lingui/react" -import { cn } from "@/lib/utils" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { Link } from "@/components/ui/link" -import { getPagePath } from "@/lib/page-path" -import { useRouter } from "next/router" -import { flexRender } from "@tanstack/react-table" -import { ColumnDef } from "@tanstack/table-core" -import { SystemRecord } from "@/lib/types" -import { IndicatorDot } from "@/components/indicator-dot" -import { AlertsButton } from "@/components/alerts-button" -import { ActionsButton } from "@/components/actions-button" -import { EyeIcon } from "@/components/icons" - -const SystemCard = memo( - ({ row, table, colLength }: { row: Row; table: TableType; colLength: number }) => { - const system = row.original - const { t } = useLingui() - - return useMemo(() => { - return ( - - -
- -
- - - {system.name} - -
-
- {table.getColumn("actions")?.getIsVisible() && ( -
- - -
- )} -
-
- - {table.getAllColumns().map((column) => { - if (!column.getIsVisible() || column.id === "system" || column.id === "actions") return null - const cell = row.getAllCells().find((cell) => cell.column.id === column.id) - if (!cell) return null - // @ts-ignore - const { Icon, name } = column.columnDef as ColumnDef - - // Special case for 'lastSeen' column: add EyeIcon before value - if (column.id === "lastSeen") { - return ( -
- -
- {name()}: -
{flexRender(cell.column.columnDef.cell, cell.getContext())}
-
-
- ) - } - - return ( -
- {Icon && } -
- {name()}: -
{flexRender(cell.column.columnDef.cell, cell.getContext())}
-
-
- ) - })} -
- - {row.original.name} - -
- ) - }, [system, colLength, t]) - } -) \ No newline at end of file diff --git a/supplemental/CHANGELOG.md b/supplemental/CHANGELOG.md index 304611d..02b9cfd 100644 --- a/supplemental/CHANGELOG.md +++ b/supplemental/CHANGELOG.md @@ -4,10 +4,16 @@ - Add status filters to All Systems table. +- Virtualize All Systems table to improve performance with hundreds of systems. (#1100) + - Fix Safari system link CSS bug. - Use older cuda image for increased compatibility (#1103) +- Truncate long system names in All Systems table. (#1104) + +- Fix update mirror and add `--china-mirrors` flag. (#1035) + ## 0.12.5 - Downgrade `gopsutil` to `v4.25.6` to fix panic on FreeBSD (#1083)