mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
975bb6d9af
* feat(perf): live runtime logs tab in Performance Probe Add a Logs tab that streams the backend runtime log ring buffer in-app, so the stdout/stderr console (unreachable on Windows without exporting) can be read live. The buffer now tracks a monotonic seq; a new tail_runtime_logs command returns lines incrementally and get_logging_mode reports the current depth. The tab has a depth switch (off/normal/debug) mirroring app settings, a line cap (500-5000), pause/clear, auto-follow, and an ordered comma-separated word filter where a plain word includes and a -word excludes, applied left to right as layers (sequence matters). * docs(changelog): note Performance Probe logs tab (PR #946) Add CHANGELOG entry and credits line for the live runtime logs tab. * fix(perf): pin log view position when scrolled up Auto-scroll keeps the logs tab at the tail, but once the user scrolls up the view now stays put — the previously-topmost line is re-pinned each tick while the log keeps appending below for further scrolling. History under the viewport is no longer trimmed while scrolled up (kept up to the ring-buffer ceiling); the cap is re-applied when following resumes. Buffer overflow is shown in the status line instead of an injected marker row. * fix(perf): scope logs tab to its own internal scroll The whole probe body scrolled (controls + filter + log) because the log container sized via height:100%, which WebKitGTK does not resolve against the flex body. Make the body a flex column with hidden overflow on the Logs tab and let the log view flex-fill, so depth/keep/pause/clear and the filter stay fixed while only the log lines scroll.
133 lines
4.5 KiB
TypeScript
133 lines
4.5 KiB
TypeScript
import { useState } from 'react';
|
|
import { Activity, ScrollText, SlidersHorizontal, X } from 'lucide-react';
|
|
import { createPortal } from 'react-dom';
|
|
import SidebarPerfProbeMonitorTab from './perfProbe/SidebarPerfProbeMonitorTab';
|
|
import SidebarPerfProbeTogglesTab from './perfProbe/SidebarPerfProbeTogglesTab';
|
|
import SidebarPerfProbeLogsTab from './perfProbe/SidebarPerfProbeLogsTab';
|
|
import { resetPerfProbeFlags, type PerfProbeFlags } from '../../utils/perf/perfFlags';
|
|
import { clearPerfLiveOverlayPins } from '../../utils/perf/perfOverlayPins';
|
|
import { resetPerfOverlayAppearance } from '../../utils/perf/perfOverlayAppearance';
|
|
import { resetPerfOverlayMode } from '../../utils/perf/perfOverlayMode';
|
|
|
|
type TabId = 'monitor' | 'toggles' | 'logs';
|
|
|
|
interface Props {
|
|
open: boolean;
|
|
onClose: () => void;
|
|
perfFlags: PerfProbeFlags;
|
|
hotCacheEnabled: boolean;
|
|
setHotCacheEnabled: (v: boolean) => void;
|
|
normalizationEngine: string;
|
|
setNormalizationEngine: (v: 'off' | 'loudness') => void;
|
|
loggingMode: string;
|
|
setLoggingMode: (v: 'off' | 'normal') => void;
|
|
}
|
|
|
|
export default function SidebarPerfProbeModal({
|
|
open,
|
|
onClose,
|
|
perfFlags,
|
|
hotCacheEnabled,
|
|
setHotCacheEnabled,
|
|
normalizationEngine,
|
|
setNormalizationEngine,
|
|
loggingMode,
|
|
setLoggingMode,
|
|
}: Props) {
|
|
const [tab, setTab] = useState<TabId>('monitor');
|
|
|
|
if (!open) return null;
|
|
|
|
const resetAll = () => {
|
|
resetPerfProbeFlags();
|
|
clearPerfLiveOverlayPins();
|
|
resetPerfOverlayAppearance();
|
|
resetPerfOverlayMode();
|
|
};
|
|
|
|
return createPortal(
|
|
<div
|
|
className="modal-overlay modal-overlay--perf-probe"
|
|
onClick={() => onClose()}
|
|
role="dialog"
|
|
aria-modal="true"
|
|
aria-labelledby="perf-probe-title"
|
|
>
|
|
<div
|
|
className="modal-content sidebar-perf-modal"
|
|
onClick={e => e.stopPropagation()}
|
|
>
|
|
<button type="button" className="modal-close" onClick={() => onClose()} aria-label="Close">
|
|
<X size={18} />
|
|
</button>
|
|
|
|
<header className="sidebar-perf-modal__header">
|
|
<h3 id="perf-probe-title" className="modal-title">Performance Probe</h3>
|
|
<p className="sidebar-perf-modal__hint">
|
|
Live metrics with optional on-screen overlays, plus diagnostic disable toggles.
|
|
</p>
|
|
</header>
|
|
|
|
<div className="sidebar-perf-modal__tabs" role="tablist" aria-label="Performance probe sections">
|
|
<button
|
|
type="button"
|
|
role="tab"
|
|
aria-selected={tab === 'monitor'}
|
|
className={`sidebar-perf-modal__tab${tab === 'monitor' ? ' sidebar-perf-modal__tab--active' : ''}`}
|
|
onClick={() => setTab('monitor')}
|
|
>
|
|
<Activity size={15} />
|
|
Monitor
|
|
</button>
|
|
<button
|
|
type="button"
|
|
role="tab"
|
|
aria-selected={tab === 'toggles'}
|
|
className={`sidebar-perf-modal__tab${tab === 'toggles' ? ' sidebar-perf-modal__tab--active' : ''}`}
|
|
onClick={() => setTab('toggles')}
|
|
>
|
|
<SlidersHorizontal size={15} />
|
|
Toggles
|
|
</button>
|
|
<button
|
|
type="button"
|
|
role="tab"
|
|
aria-selected={tab === 'logs'}
|
|
className={`sidebar-perf-modal__tab${tab === 'logs' ? ' sidebar-perf-modal__tab--active' : ''}`}
|
|
onClick={() => setTab('logs')}
|
|
>
|
|
<ScrollText size={15} />
|
|
Logs
|
|
</button>
|
|
</div>
|
|
|
|
<div className={`sidebar-perf-modal__body${tab === 'logs' ? ' sidebar-perf-modal__body--logs' : ''}`}>
|
|
{tab === 'monitor' && <SidebarPerfProbeMonitorTab />}
|
|
{tab === 'toggles' && (
|
|
<SidebarPerfProbeTogglesTab
|
|
perfFlags={perfFlags}
|
|
hotCacheEnabled={hotCacheEnabled}
|
|
setHotCacheEnabled={setHotCacheEnabled}
|
|
normalizationEngine={normalizationEngine}
|
|
setNormalizationEngine={setNormalizationEngine}
|
|
loggingMode={loggingMode}
|
|
setLoggingMode={setLoggingMode}
|
|
/>
|
|
)}
|
|
{tab === 'logs' && <SidebarPerfProbeLogsTab />}
|
|
</div>
|
|
|
|
<div className="sidebar-perf-modal__actions">
|
|
<button type="button" className="btn btn-ghost" onClick={resetAll}>
|
|
Reset all
|
|
</button>
|
|
<button type="button" className="btn btn-primary" onClick={() => onClose()}>
|
|
Close
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>,
|
|
document.body,
|
|
);
|
|
}
|