Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ module.exports = {
'warn',
{ allowConstantExport: true },
],
// React Compiler-readiness rule (added in eslint-plugin-react-hooks v7);
// this project doesn't use the Compiler, and it flags the standard
// "reset state, then kick off an async load" effect pattern used
// throughout src/components as an error.
'react-hooks/set-state-in-effect': 'off',
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
Expand Down
59 changes: 53 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"electron": "^42.5.0",
"electron-builder": "^26.15.3",
"eslint": "^8.57.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-hooks": "^7.1.1",
"eslint-plugin-react-refresh": "^0.4.6",
"jsdom": "^29.1.1",
"postcss": "^8.4.38",
Expand Down
2 changes: 2 additions & 0 deletions src/components/LogTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ export function LogTable({ entries, isStream = false, onPidClick, persist = fals

const parentRef = useRef<HTMLDivElement>(null)
const followRef = useRef(true)
// TanStack Virtual's returned functions are documented as stable; this is the library's normal API shape.
// eslint-disable-next-line react-hooks/incompatible-library
const virtualizer = useVirtualizer({
count: sorted.length,
getScrollElement: () => parentRef.current,
Expand Down
48 changes: 30 additions & 18 deletions src/components/MetricsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ const Tip = ({
)
}

function SortTh({ label, k, align = 'right', cls = '', sortKey, sortDir, onToggle }: {
label: string
k: keyof ProcRow
align?: 'left' | 'right'
cls?: string
sortKey: keyof ProcRow | null
sortDir: 'asc' | 'desc'
onToggle: (k: keyof ProcRow) => void
}) {
return (
<th
onClick={() => onToggle(k)}
className={`${align === 'left' ? 'text-left' : 'text-right'} py-2 px-3 font-medium cursor-pointer select-none hover:text-body ${sortKey === k ? 'text-accent' : ''} ${cls}`}
>
<span className="inline-flex items-center gap-1">{label}
{sortKey === k && (sortDir === 'asc' ? <ArrowUp size={11} /> : <ArrowDown size={11} />)}
</span>
</th>
)
}

function Card({ children, className = '' }: { children: React.ReactNode; className?: string }) {
return <div className={`bg-panel border border-border rounded-xl p-4 ${className}`}>{children}</div>
}
Expand Down Expand Up @@ -153,16 +174,7 @@ export function MetricsPage({ entries, loading, onOpenProcess }: MetricsPageProp
const axisTick = { fontSize: 10, fill: c.axis }
const catTick = { fontSize: 10, fill: c.legend }

const SortTh = ({ label, k, align = 'right', cls = '' }: { label: string; k: keyof ProcRow; align?: 'left' | 'right'; cls?: string }) => (
<th
onClick={() => toggle(k)}
className={`${align === 'left' ? 'text-left' : 'text-right'} py-2 px-3 font-medium cursor-pointer select-none hover:text-body ${sortKey === k ? 'text-accent' : ''} ${cls}`}
>
<span className="inline-flex items-center gap-1">{label}
{sortKey === k && (sortDir === 'asc' ? <ArrowUp size={11} /> : <ArrowDown size={11} />)}
</span>
</th>
)
const sortThProps = { sortKey, sortDir, onToggle: toggle } as const

return (
<div className="flex-1 overflow-y-auto p-6 space-y-6">
Expand Down Expand Up @@ -197,14 +209,14 @@ export function MetricsPage({ entries, loading, onOpenProcess }: MetricsPageProp
<table className="w-full text-xs">
<thead className="sticky top-0 bg-panel z-10">
<tr className="border-b border-border text-dim uppercase tracking-wider text-[10px]">
<SortTh label="Process" k="name" align="left" />
<SortTh label="PID" k="pid" />
<SortTh label="Run as" k="isRoot" />
<SortTh label="Faults" k="faults" cls="text-fault" />
<SortTh label="Errors" k="errors" cls="text-error" />
<SortTh label="Debug" k="debug" cls="text-debug" />
<SortTh label="Total" k="total" />
<SortTh label="Error %" k="ratio" />
<SortTh label="Process" k="name" align="left" {...sortThProps} />
<SortTh label="PID" k="pid" {...sortThProps} />
<SortTh label="Run as" k="isRoot" {...sortThProps} />
<SortTh label="Faults" k="faults" cls="text-fault" {...sortThProps} />
<SortTh label="Errors" k="errors" cls="text-error" {...sortThProps} />
<SortTh label="Debug" k="debug" cls="text-debug" {...sortThProps} />
<SortTh label="Total" k="total" {...sortThProps} />
<SortTh label="Error %" k="ratio" {...sortThProps} />
</tr>
</thead>
<tbody>
Expand Down
32 changes: 22 additions & 10 deletions src/components/ProcessesView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ interface ProcessesViewProps {

type Row = ProcessListItem & { logs: number }

function Th({ label, k, align = 'right', mode, sortKey, dir, onToggle }: {
label: string
k: keyof Row
align?: 'left' | 'right'
mode: 'list' | 'tree'
sortKey: keyof Row | null
dir: 'asc' | 'desc'
onToggle: (k: keyof Row) => void
}) {
return (
<th onClick={() => mode === 'list' && onToggle(k)}
className={`${align === 'left' ? 'text-left' : 'text-right'} py-2 px-3 font-medium whitespace-nowrap ${mode === 'list' ? 'cursor-pointer hover:text-body' : ''} ${sortKey === k && mode === 'list' ? 'text-accent' : ''}`}>
<span className="inline-flex items-center gap-1">{label}{mode === 'list' && sortKey === k && (dir === 'asc' ? <ArrowUp size={11} /> : <ArrowDown size={11} />)}</span>
</th>
)
}

export function ProcessesView({ entries, onOpenProcess }: ProcessesViewProps) {
const [procs, setProcs] = useState<ProcessListItem[]>([])
const [loading, setLoading] = useState(false)
Expand Down Expand Up @@ -78,12 +95,7 @@ export function ProcessesView({ entries, onOpenProcess }: ProcessesViewProps) {
return out
}, [mode, procs, search, runAs, matchesFilter])

const Th = ({ label, k, align = 'right' }: { label: string; k: keyof Row; align?: 'left' | 'right' }) => (
<th onClick={() => mode === 'list' && toggle(k)}
className={`${align === 'left' ? 'text-left' : 'text-right'} py-2 px-3 font-medium whitespace-nowrap ${mode === 'list' ? 'cursor-pointer hover:text-body' : ''} ${key === k && mode === 'list' ? 'text-accent' : ''}`}>
<span className="inline-flex items-center gap-1">{label}{mode === 'list' && key === k && (dir === 'asc' ? <ArrowUp size={11} /> : <ArrowDown size={11} />)}</span>
</th>
)
const thProps = { mode, sortKey: key, dir, onToggle: toggle } as const

const renderRow = (p: Row | ProcessListItem, depth = 0) => {
const logs = 'logs' in p ? p.logs : (logCounts.get(p.pid) ?? 0)
Expand Down Expand Up @@ -134,10 +146,10 @@ export function ProcessesView({ entries, onOpenProcess }: ProcessesViewProps) {
<table className="w-full text-xs">
<thead className="sticky top-0 bg-void z-10">
<tr className="border-b border-border text-dim uppercase tracking-wider text-[10px]">
<Th label={mode === 'tree' ? 'Process tree' : 'Process'} k="name" align="left" />
<Th label="PID" k="pid" /><Th label="User" k="user" align="left" />
<Th label="% CPU" k="cpu" /><Th label="% Mem" k="mem" /><Th label="Memory" k="rss" />
<Th label="State" k="stat" align="left" /><Th label="Run as" k="isRoot" /><Th label="Log entries" k="logs" />
<Th label={mode === 'tree' ? 'Process tree' : 'Process'} k="name" align="left" {...thProps} />
<Th label="PID" k="pid" {...thProps} /><Th label="User" k="user" align="left" {...thProps} />
<Th label="% CPU" k="cpu" {...thProps} /><Th label="% Mem" k="mem" {...thProps} /><Th label="Memory" k="rss" {...thProps} />
<Th label="State" k="stat" align="left" {...thProps} /><Th label="Run as" k="isRoot" {...thProps} /><Th label="Log entries" k="logs" {...thProps} />
</tr>
</thead>
<tbody>
Expand Down