diff --git a/README.md b/README.md index 1b9c9196..cb71e74a 100644 --- a/README.md +++ b/README.md @@ -63,14 +63,14 @@ Pachtop is built with what I'd like to call the **"VRRTT"** stack (Vite, Rust, R - **SysInfo** is a Rust crate that provides system information. This crate is used to get information about the system's CPU, memory, disks, network, and processes. This is what Pachtop uses to get the system metrics on different operating systems. # Features & Roadmap - +- [x] Themes & Color Schemes - [x] Aggregate CPU usage & per-core CPU usage metrics - [x] Memory Usage - [x] Network Usage - [x] System Information - [x] Processes - [x] Disk Usage -- [x] Persistent metrics over time +- [ ] Persistent metrics over time - [ ] Battery Usage? - [ ] GPU Usage? diff --git a/src/components/area-chart.tsx b/src/components/area-chart.tsx index 4d81b3ca..41bbdaf9 100644 --- a/src/components/area-chart.tsx +++ b/src/components/area-chart.tsx @@ -2,7 +2,8 @@ import * as Highcharts from "highcharts/highstock"; import HighchartsReact from "highcharts-react-official"; import { Dispatch, SetStateAction, useEffect, useRef } from "react"; import { useState } from "react"; -import { useViewportSize } from "@mantine/hooks"; +import { useColorScheme, useViewportSize } from "@mantine/hooks"; +import { useMantineTheme } from "@mantine/core"; export interface InitialAreaChatStateInput { title: { @@ -23,6 +24,7 @@ export interface InitialAreaChatStateInput { export const useAreaChartState = ( opts: InitialAreaChatStateInput ): [Highcharts.Options, Dispatch>] => { + const theme = useMantineTheme(); const [chartOptions, setChartOptions] = useState({ title: { text: opts.title.text, diff --git a/src/components/page-wrapper.tsx b/src/components/page-wrapper.tsx index 791da2bb..af1ba2b6 100644 --- a/src/components/page-wrapper.tsx +++ b/src/components/page-wrapper.tsx @@ -3,11 +3,12 @@ import { Stack, Title } from "@mantine/core"; interface PageWrapperProps { name: string; children: React.ReactNode; + height?: number | string; } -const PageWrapper: React.FC = ({ children, name }) => { +const PageWrapper: React.FC = ({ children, name, height }) => { return ( - + {name} {children} diff --git a/src/features/metrics/components/disks/disk.stats-ring.tsx b/src/features/metrics/components/disks/disk.stats-ring.tsx index c87d723b..8d319a97 100644 --- a/src/features/metrics/components/disks/disk.stats-ring.tsx +++ b/src/features/metrics/components/disks/disk.stats-ring.tsx @@ -4,9 +4,11 @@ import formatOverallStats from "@/features/metrics/utils/format-overall-stats"; import React from "react"; import { IconCpu2 } from "@tabler/icons-react"; +import { useMantineTheme } from "@mantine/core"; const DiskStatsRing: React.FC = ({}) => { const { disks } = useServerEventsContext(); + const { other } = useMantineTheme(); const disk = disks[0]; @@ -18,7 +20,9 @@ const DiskStatsRing: React.FC = ({}) => { const stats = React.useMemo(() => formatOverallStats(used, available), [used, available]); const label = `Disk ${disk?.data?.at(-1)?.name}`; - return ; + return ( + + ); }; export default DiskStatsRing; diff --git a/src/features/metrics/components/global-cpu/global-cpu.area-chart.tsx b/src/features/metrics/components/global-cpu/global-cpu.area-chart.tsx index 08a4a2de..1c3d1534 100644 --- a/src/features/metrics/components/global-cpu/global-cpu.area-chart.tsx +++ b/src/features/metrics/components/global-cpu/global-cpu.area-chart.tsx @@ -3,6 +3,7 @@ import Card from "@/components/card"; import AreaChart, { useAreaChartState } from "@/components/area-chart"; import useServerEventsContext from "@/hooks/useServerEventsContext"; import { useEffect } from "react"; +import { useMantineTheme } from "@mantine/core"; // TODO: Remove Luxon and ChartJS // TODO: Make timestamp work automatically @@ -10,6 +11,7 @@ import { useEffect } from "react"; const GlobalCpuAreaChart: React.FC = ({}) => { const { globalCpu } = useServerEventsContext(); + const { other } = useMantineTheme(); const [chartOptions, setChartOptions] = useAreaChartState({ title: { text: "CPU Usage", @@ -34,13 +36,7 @@ const GlobalCpuAreaChart: React.FC = ({}) => { name: "CPU Usage", type: "area", data: globalCpu.map((cpu) => [cpu.timestamp, cpu.usage]), - color: { - linearGradient: { x1: 0, x2: 0, y1: 0, y2: 1 }, - stops: [ - [0, "rgba(255, 99, 132, 1)"], - [1, "rgba(255, 99, 132, 0.45)"], - ], - }, + color: other.charts.area.globalCpu.color, }, ], }); diff --git a/src/features/metrics/components/global-cpu/global-cpu.stats-ring.tsx b/src/features/metrics/components/global-cpu/global-cpu.stats-ring.tsx index 3da888bc..81be25fc 100644 --- a/src/features/metrics/components/global-cpu/global-cpu.stats-ring.tsx +++ b/src/features/metrics/components/global-cpu/global-cpu.stats-ring.tsx @@ -4,14 +4,16 @@ import fromNumberToPercentageString from "@/features/metrics/utils/from-number-t import React from "react"; import { IconCpu } from "@tabler/icons-react"; +import { useMantineTheme } from "@mantine/core"; const GlobalCpuStatsRing: React.FC = ({}) => { const { globalCpu } = useServerEventsContext(); + const { other } = useMantineTheme(); const progress = globalCpu.at(-1)?.usage || 0; const stats = React.useMemo(() => fromNumberToPercentageString(progress), [progress]); - return ; + return ; }; export default GlobalCpuStatsRing; diff --git a/src/features/metrics/components/memory/memory.area-chart.tsx b/src/features/metrics/components/memory/memory.area-chart.tsx index c10cb560..009be06d 100644 --- a/src/features/metrics/components/memory/memory.area-chart.tsx +++ b/src/features/metrics/components/memory/memory.area-chart.tsx @@ -3,9 +3,11 @@ import formatBytes from "@/features/metrics/utils/format-bytes"; import AreaChart, { useAreaChartState } from "@/components/area-chart"; import useServerEventsContext from "@/hooks/useServerEventsContext"; import { useEffect } from "react"; +import { useMantineTheme } from "@mantine/core"; const MemoryAreaChart: React.FC = ({}) => { const { memory } = useServerEventsContext(); + const { other } = useMantineTheme(); const [chartOptions, setChartOptions] = useAreaChartState({ title: { text: "Ram Usage", @@ -31,13 +33,7 @@ const MemoryAreaChart: React.FC = ({}) => { name: "RAM Usage", type: "area", data: memory.map((mem) => [mem.timestamp, mem.used]), - color: { - linearGradient: { x1: 0, x2: 0, y1: 0, y2: 1 }, - stops: [ - [0, "rgba(10, 167, 147, 1)"], - [1, "rgba(10, 167, 147, 0.45)"], - ], - }, + color: other.charts.area.memory.color, }, ], }); diff --git a/src/features/metrics/components/memory/memory.stats-ring.tsx b/src/features/metrics/components/memory/memory.stats-ring.tsx index 95f7c3be..6569d0ef 100644 --- a/src/features/metrics/components/memory/memory.stats-ring.tsx +++ b/src/features/metrics/components/memory/memory.stats-ring.tsx @@ -4,9 +4,11 @@ import formatOverallStats from "@/features/metrics/utils/format-overall-stats"; import React from "react"; import { IconCpu2 } from "@tabler/icons-react"; +import { useMantineTheme } from "@mantine/core"; const MemoryStatsRing: React.FC = ({}) => { const { memory } = useServerEventsContext(); + const { other } = useMantineTheme(); const available = memory?.at(-1)?.total || 0; const used = memory?.at(-1)?.used || 0; @@ -14,7 +16,9 @@ const MemoryStatsRing: React.FC = ({}) => { const stats = React.useMemo(() => formatOverallStats(used, available), [used, available]); - return ; + return ( + + ); }; export default MemoryStatsRing; diff --git a/src/features/metrics/components/networks/networks.area-chart.tsx b/src/features/metrics/components/networks/networks.area-chart.tsx index 98c9119c..b88a23e1 100644 --- a/src/features/metrics/components/networks/networks.area-chart.tsx +++ b/src/features/metrics/components/networks/networks.area-chart.tsx @@ -4,6 +4,7 @@ import AreaChart, { useAreaChartState } from "@/components/area-chart"; import useServerEventsContext from "@/hooks/useServerEventsContext"; import { useEffect, useState } from "react"; import { SeriesOptionsType } from "highcharts"; +import { useMantineTheme } from "@mantine/core"; // TODO: Remove Luxon and ChartJS // TODO: Make timestamp work automatically @@ -11,6 +12,7 @@ import { SeriesOptionsType } from "highcharts"; const NetworksAreaChart: React.FC = ({}) => { const { networks } = useServerEventsContext(); + const { other } = useMantineTheme(); const [chartOptions, setChartOptions] = useAreaChartState({ title: { text: "Network Received", @@ -40,12 +42,11 @@ const NetworksAreaChart: React.FC = ({}) => { name: `${network.id}`, type: "area", data: network.data.map((net) => [net.timestamp, net.received]), + color: other.charts.area.networkReceived.color, })), }); }, [JSON.stringify(networks)]); - - return ( diff --git a/src/features/metrics/components/swap/swap.area-chart.tsx b/src/features/metrics/components/swap/swap.area-chart.tsx index a5be6b8e..34d16b7a 100644 --- a/src/features/metrics/components/swap/swap.area-chart.tsx +++ b/src/features/metrics/components/swap/swap.area-chart.tsx @@ -3,9 +3,11 @@ import formatBytes from "@/features/metrics/utils/format-bytes"; import AreaChart, { useAreaChartState } from "@/components/area-chart"; import useServerEventsContext from "@/hooks/useServerEventsContext"; import { useEffect } from "react"; +import { useMantineTheme } from "@mantine/core"; const SwapAreaChart: React.FC = ({}) => { const { swap } = useServerEventsContext(); + const { other } = useMantineTheme(); const [chartOptions, setChartOptions] = useAreaChartState({ title: { text: "Swap Memory Usage", @@ -31,13 +33,7 @@ const SwapAreaChart: React.FC = ({}) => { name: "Swap Usage", type: "area", data: swap.map((swap) => [swap.timestamp, swap.used]), - color: { - linearGradient: { x1: 0, x2: 0, y1: 0, y2: 1 }, - stops: [ - [0, "rgba(53, 162, 235, 0.45)"], - [1, "rgba(53, 162, 235)"], - ], - }, + color: other.charts.area.swap.color, }, ], }); diff --git a/src/features/metrics/components/swap/swap.stats-ring.tsx b/src/features/metrics/components/swap/swap.stats-ring.tsx index 579821fc..014611c8 100644 --- a/src/features/metrics/components/swap/swap.stats-ring.tsx +++ b/src/features/metrics/components/swap/swap.stats-ring.tsx @@ -4,9 +4,11 @@ import formatOverallStats from "@/features/metrics/utils/format-overall-stats"; import React from "react"; import { IconCpu2 } from "@tabler/icons-react"; +import { useMantineTheme } from "@mantine/core"; const SwapStatsRing: React.FC = ({}) => { const { swap } = useServerEventsContext(); + const { other } = useMantineTheme(); const available = swap?.at(-1)?.total || 0; const used = swap?.at(-1)?.used || 0; @@ -14,7 +16,9 @@ const SwapStatsRing: React.FC = ({}) => { const stats = React.useMemo(() => formatOverallStats(used, available), [used, available]); - return ; + return ( + + ); }; export default SwapStatsRing; diff --git a/src/features/settings/pages/settings.page.tsx b/src/features/settings/pages/settings.page.tsx index b8424ab2..5c90eb0c 100644 --- a/src/features/settings/pages/settings.page.tsx +++ b/src/features/settings/pages/settings.page.tsx @@ -1,27 +1,30 @@ import React from "react"; -import AutoStartSettingsView from "@/features/settings/views/autostart.view"; +import GeneralSettingsView from "@/features/settings/views/general.view"; import AboutView from "@/features/settings/views/about.view"; import PageWrapper from "@/components/page-wrapper"; import Card from "@/components/card"; -import { Icon24Hours, IconGitBranch } from "@tabler/icons-react"; +import { IconGitBranch, IconSettings2 } from "@tabler/icons-react"; import { Center, Grid, NavLink } from "@mantine/core"; import { useState } from "react"; const settings = [ - { icon: Icon24Hours, label: "Auto Start", view: }, - { - icon: IconGitBranch, - label: "About", - view: , - }, + { icon: IconSettings2, label: "General", view: }, + + // TODO: FIX responsiveness + // { + // icon: IconGitBranch, + // label: "About", + // view: , + // }, ]; const SettingsPage = () => { const [active, setActive] = useState(0); + const items = settings.map((item, index) => ( - + { )); return ( - +
- - {items} + + {items}
diff --git a/src/features/settings/views/autostart.view.tsx b/src/features/settings/views/autostart.view.tsx deleted file mode 100644 index 3ba50faa..00000000 --- a/src/features/settings/views/autostart.view.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Skeleton, Switch } from "@mantine/core"; -import { useEffect, useState } from "react"; -import { autostart } from "@/lib"; - -const AutoStartSettingsView = () => { - const [checked, setChecked] = useState(false); - const [loading, setLoading] = useState(true); - const checkAutoStart = async () => setChecked(await autostart.isEnabled()); - - useEffect(() => { - checkAutoStart(); - setLoading(false); - }, [checkAutoStart]); - - const onChange = () => { - if (!checked) { - autostart.enable(); - } else { - autostart.disable(); - } - setChecked(!checked); - }; - - if (loading) { - return ( - <> - - - - - ); - } - return ; -}; - -export default AutoStartSettingsView; diff --git a/src/features/settings/views/general.view.tsx b/src/features/settings/views/general.view.tsx new file mode 100644 index 00000000..d2249f0e --- /dev/null +++ b/src/features/settings/views/general.view.tsx @@ -0,0 +1,60 @@ +import { Grid, Select, Skeleton, Space, Switch } from "@mantine/core"; +import { useEffect, useState } from "react"; +import { autostart } from "@/lib"; +import { useTheme } from "@/hooks/useTheme"; +import { THEME_OPTION } from "../../../providers/theme.provider"; + +const GeneralSettingsView = () => { + const [checked, setChecked] = useState(false); + const [loading, setLoading] = useState(true); + const checkAutoStart = async () => setChecked(await autostart.isEnabled()); + const { setTheme, currentTheme } = useTheme(); + + useEffect(() => { + checkAutoStart(); + setLoading(false); + }, [checkAutoStart]); + + const onChange = () => { + if (!checked) { + autostart.enable(); + } else { + autostart.disable(); + } + setChecked(!checked); + }; + + if (loading) { + return ( + <> + + + + + ); + } + return ( + <> + + + +