1// SPDX-License-Identifier: GPL-2.0-only
2// Copyright (c) 2026-present FERS Contributors (see AUTHORS.md).
4import { create } from 'zustand';
6export type FersLogLevel =
16export type FersLogEntry = {
22export type ConfigurableFersLogLevel = Exclude<FersLogLevel, 'UNKNOWN'>;
25 entries: FersLogEntry[];
28 logLevel: ConfigurableFersLogLevel;
30 appendLog: (entry: FersLogEntry) => void;
31 clearLogs: () => void;
32 setMaxLines: (maxLines: number) => void;
33 setLogLevel: (logLevel: ConfigurableFersLogLevel) => void;
34 setOpen: (isOpen: boolean) => void;
35 toggleOpen: () => void;
38export const DEFAULT_LOG_MAX_LINES = 2000;
39export const MIN_LOG_MAX_LINES = 100;
40export const MAX_LOG_MAX_LINES = 20_000;
41export const DEFAULT_LOG_LEVEL: ConfigurableFersLogLevel = 'INFO';
42export const LOG_LEVEL_OPTIONS: ConfigurableFersLogLevel[] = [
52export const isConfigurableFersLogLevel = (
54): level is ConfigurableFersLogLevel =>
55 LOG_LEVEL_OPTIONS.some((option) => option === level);
57export const clampLogMaxLines = (maxLines: number) => {
58 if (!Number.isFinite(maxLines)) {
59 return DEFAULT_LOG_MAX_LINES;
64 Math.min(MAX_LOG_MAX_LINES, Math.floor(maxLines))
69 entries: FersLogEntry[],
71): { entries: FersLogEntry[]; dropped: number } => {
72 if (entries.length <= maxLines) {
73 return { entries, dropped: 0 };
77 entries: entries.slice(entries.length - maxLines),
78 dropped: entries.length - maxLines,
82export const useFersLogStore = create<FersLogStore>()((set) => ({
85 maxLines: DEFAULT_LOG_MAX_LINES,
86 logLevel: DEFAULT_LOG_LEVEL,
90 const trimmed = trimEntries(
91 [...state.entries, entry],
96 entries: trimmed.entries,
97 droppedCount: state.droppedCount + trimmed.dropped,
100 clearLogs: () => set({ entries: [], droppedCount: 0 }),
101 setMaxLines: (maxLines) =>
103 const nextMaxLines = clampLogMaxLines(maxLines);
104 const trimmed = trimEntries(state.entries, nextMaxLines);
107 maxLines: nextMaxLines,
108 entries: trimmed.entries,
109 droppedCount: state.droppedCount + trimmed.dropped,
112 setLogLevel: (logLevel) => set({ logLevel }),
113 setOpen: (isOpen) => set({ isOpen }),
114 toggleOpen: () => set((state) => ({ isOpen: !state.isOpen })),