Add settings-configurable cap on logs kept in memory (ticket 026)

Settings.max_logs (default None = unlimited) is pushed to the engine
via a new EngineMessage::SetMaxLogs, enforced on every ingest and
whenever the setting changes at runtime (no restart required).
Eviction drops the oldest (lowest-sequence) logs; sequence numbering
isn't renumbered and buckets already derived from an evicted log are
left in place. Exposed via PATCH /api/settings (max_logs: number |
null) and a NavMenu numeric field (blank = unlimited).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
Hugh Kaznowski
2026-09-05 15:12:12 +01:00
co-authored by Claude Sonnet 5
parent aaa5596030
commit 1d0f324c43
8 changed files with 261 additions and 16 deletions
+36 -1
View File
@@ -1,4 +1,4 @@
import { useState } from 'react'
import { useEffect, useState } from 'react'
import type { Stats } from '../lib/api'
import type { ColorScheme } from '../lib/api'
import { useSettingsPanel } from '../lib/useSettingsPanel'
@@ -120,6 +120,25 @@ export function NavMenu({ stats }: { stats: Stats | null }) {
function SettingsPanel({ panel }: { panel: ReturnType<typeof useSettingsPanel> }) {
const [filename, setFilename] = useState('')
const [content, setContent] = useState('')
const [maxLogsInput, setMaxLogsInput] = useState('')
useEffect(() => {
setMaxLogsInput(panel.settings?.max_logs != null ? String(panel.settings.max_logs) : '')
}, [panel.settings?.max_logs])
function handleMaxLogsBlur() {
const trimmed = maxLogsInput.trim()
if (trimmed === '') {
panel.setMaxLogs(null)
return
}
const parsed = Number(trimmed)
if (!Number.isFinite(parsed) || parsed <= 0) {
setMaxLogsInput(panel.settings?.max_logs != null ? String(panel.settings.max_logs) : '')
return
}
panel.setMaxLogs(Math.floor(parsed))
}
async function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0]
@@ -158,6 +177,22 @@ function SettingsPanel({ panel }: { panel: ReturnType<typeof useSettingsPanel> }
</div>
</section>
<section>
<h3 className="mb-2 text-xs font-semibold uppercase tracking-wide text-text-subtle">Log retention</h3>
<div className="flex items-center gap-2">
<input
type="number"
min={1}
placeholder="Unlimited"
value={maxLogsInput}
onChange={(e) => setMaxLogsInput(e.target.value)}
onBlur={handleMaxLogsBlur}
className="w-28 rounded border border-border-soft bg-surface-1 px-2 py-1 text-xs text-text placeholder:text-text-subtle focus:border-border-strong focus:outline-none"
/>
<span className="text-xs text-text-faint">max logs kept in memory (blank = unlimited)</span>
</div>
</section>
<section>
<h3 className="mb-2 text-xs font-semibold uppercase tracking-wide text-text-subtle">Import CSS</h3>
<form onSubmit={handleImport} className="flex flex-col gap-2">