fix(ui): implement UI review findings — a11y, IA, and consistency fixes

Fixes the reviewed gaps: keyboard-inaccessible delete controls (SessionRail,
Entities row), case-sensitive entity filter, two competing entity-detail
navigation patterns (standardize on EntitySheet), non-clickable Overview KPI
cards, a bare button bypassing the shared Button component, inconsistent
blur-only vs live filtering, and an unenforced sanitization assumption on
search snippet HTML (now using the already-present dompurify dependency).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 21:52:59 +02:00
parent b72267bd72
commit fb4c76ba82
12 changed files with 404 additions and 88 deletions

View File

@@ -21,6 +21,16 @@ export function relativeTime(iso: string | null | undefined): string {
return `${d}d ago`;
}
// debounce wraps fn so rapid calls (e.g. keystrokes in a filter input)
// collapse into one invocation after `wait`ms of silence.
export function debounce<T extends (...args: never[]) => void>(fn: T, wait = 300): T {
let timer: ReturnType<typeof setTimeout> | undefined;
return ((...args: Parameters<T>) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), wait);
}) as T;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type WithoutChild<T> = T extends { child?: any } ? Omit<T, "child"> : T;
// eslint-disable-next-line @typescript-eslint/no-explicit-any