optimizations

This commit is contained in:
proddy
2025-10-28 22:19:08 +01:00
parent 55b893362c
commit 3abfb7bb9c
93 changed files with 3953 additions and 3361 deletions

View File

@@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from 'react';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { toast } from 'react-toastify';
import DownloadIcon from '@mui/icons-material/GetApp';
@@ -31,6 +31,8 @@ import type { LogEntry, LogSettings } from 'types';
import { LogLevel } from 'types';
import { updateValueDirty, useRest } from 'utils';
const MAX_LOG_ENTRIES = 1000; // Limit log entries to prevent memory issues
const TextColors: Record<LogLevel, string> = {
[LogLevel.ERROR]: '#ff0000', // red
[LogLevel.WARNING]: '#ff0000', // red
@@ -47,11 +49,6 @@ const LogEntryLine = styled('span')(
})
);
const topOffset = () =>
document.getElementById('log-window')?.getBoundingClientRect().bottom || 0;
const leftOffset = () =>
document.getElementById('log-window')?.getBoundingClientRect().left || 0;
const levelLabel = (level: LogLevel) => {
switch (level) {
case LogLevel.ERROR:
@@ -71,6 +68,36 @@ const levelLabel = (level: LogLevel) => {
}
};
// Memoized log entry component to prevent unnecessary re-renders
const LogEntryItem = memo(
({ entry, compact }: { entry: LogEntry; compact: boolean }) => {
const paddedLevelLabel = (level: LogLevel) => {
const label = levelLabel(level);
return compact ? ' ' + label[0] : label.padStart(8, '\xa0');
};
const paddedNameLabel = (name: string) => {
const label = '[' + name + ']';
return compact ? label : label.padEnd(12, '\xa0');
};
const paddedIDLabel = (id: number) => {
const label = id + ':';
return compact ? label : label.padEnd(7, '\xa0');
};
return (
<div style={{ font: '14px monospace', whiteSpace: 'nowrap' }}>
<span>{entry.t}</span>
<span>{paddedLevelLabel(entry.l)}&nbsp;</span>
<span>{paddedIDLabel(entry.i)} </span>
<span>{paddedNameLabel(entry.n)} </span>
<LogEntryLine details={{ level: entry.l }}>{entry.m}</LogEntryLine>
</div>
);
}
);
const SystemLog = () => {
const { LL } = useI18nContext();
@@ -107,7 +134,7 @@ const SystemLog = () => {
const ALPHA_NUMERIC_DASH_REGEX = /^[a-fA-F0-9 ]+$/;
const updateFormValue = updateValueDirty(
origData,
origData as unknown as Record<string, unknown>,
dirtyFlags,
setDirtyFlags,
updateDataValue as (value: unknown) => void
@@ -121,7 +148,13 @@ const SystemLog = () => {
const rawData = message.data;
const logentry = JSON.parse(rawData) as LogEntry;
if (lastId < logentry.i) {
setLogEntries((log) => [...log, logentry]);
setLogEntries((log) => {
const newLog = [...log, logentry];
// Limit log entries to prevent memory issues
return newLog.length > MAX_LOG_ENTRIES
? newLog.slice(-MAX_LOG_ENTRIES)
: newLog;
});
setLastId(logentry.i);
}
})
@@ -129,27 +162,11 @@ const SystemLog = () => {
toast.error('No connection to Log service');
});
const paddedLevelLabel = (level: LogLevel) => {
const label = levelLabel(level);
return data?.compact ? ' ' + label[0] : label.padStart(8, '\xa0');
};
const onDownload = useCallback(() => {
const result = logEntries
.map((i) => `${i.t} ${levelLabel(i.l)} ${i.i}: [${i.n}] ${i.m}`)
.join('\n');
const paddedNameLabel = (name: string) => {
const label = '[' + name + ']';
return data?.compact ? label : label.padEnd(12, '\xa0');
};
const paddedIDLabel = (id: number) => {
const label = id + ':';
return data?.compact ? label : label.padEnd(7, '\xa0');
};
const onDownload = () => {
let result = '';
for (const i of logEntries) {
result +=
i.t + ' ' + levelLabel(i.l) + ' ' + i.i + ': [' + i.n + '] ' + i.m + '\n';
}
const a = document.createElement('a');
a.setAttribute(
'href',
@@ -159,11 +176,11 @@ const SystemLog = () => {
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
}, [logEntries]);
const saveSettings = async () => {
const saveSettings = useCallback(async () => {
await saveData();
};
}, [saveData]);
// handle scrolling
const ref = useRef<HTMLDivElement>(null);
@@ -174,9 +191,9 @@ const SystemLog = () => {
block: 'end'
});
}
}, [logEntries.length]);
}, [logEntries.length, autoscroll]);
const sendReadCommand = () => {
const sendReadCommand = useCallback(() => {
if (readValue === '') {
setReadOpen(!readOpen);
return;
@@ -187,7 +204,17 @@ const SystemLog = () => {
setReadOpen(false);
setReadValue('');
}
};
}, [readValue, readOpen, send]);
// Memoize box positioning to avoid recalculating on every render
const boxPosition = useMemo(() => {
const logWindow = document.getElementById('log-window');
if (!logWindow) {
return { top: 0, left: 0 };
}
const rect = logWindow.getBoundingClientRect();
return { top: rect.bottom, left: rect.left };
}, [data]); // Recalculate only when data changes (settings may affect layout)
const content = () => {
if (!data) {
@@ -332,21 +359,13 @@ const SystemLog = () => {
position: 'absolute',
right: 18,
bottom: 18,
left: () => leftOffset(),
top: () => topOffset(),
left: boxPosition.left,
top: boxPosition.top - 110,
p: 1
}}
>
{logEntries.map((e) => (
<div key={e.i} style={{ font: '14px monospace', whiteSpace: 'nowrap' }}>
<span>{e.t}</span>
<span>{paddedLevelLabel(e.l)}&nbsp;</span>
<span>{paddedIDLabel(e.i)} </span>
<span>{paddedNameLabel(e.n)} </span>
<LogEntryLine details={{ level: e.l }} key={e.i}>
{e.m}
</LogEntryLine>
</div>
<LogEntryItem key={e.i} entry={e} compact={data.compact} />
))}
<div ref={ref} />