feat: major UI overhaul - 1/3+2/3 layout, animated logo, history drawer, editable titles, Czech, dark default, playful design

This commit is contained in:
2026-06-03 23:06:56 +02:00
parent 8d09b17467
commit c3e167bde9
11 changed files with 678 additions and 437 deletions
+90 -52
View File
@@ -12,11 +12,13 @@ import {
import type { InputValues } from "../lib/types";
import { useI18n } from "../lib/i18n";
interface HistoryPanelProps {
interface HistoryDrawerProps {
open: boolean;
onClose: () => void;
onLoad: (entry: HistoryEntry) => void;
}
export function HistoryPanel({ onLoad }: HistoryPanelProps) {
export function HistoryDrawer({ open, onClose, onLoad }: HistoryDrawerProps) {
const [entries, setEntries] = useState<HistoryEntry[]>([]);
const { t } = useI18n();
@@ -25,63 +27,96 @@ export function HistoryPanel({ onLoad }: HistoryPanelProps) {
}, []);
useEffect(() => {
const handler = () => {
setEntries(loadHistory());
};
const handler = () => setEntries(loadHistory());
window.addEventListener(HISTORY_UPDATED_EVENT, handler);
return () => window.removeEventListener(HISTORY_UPDATED_EVENT, handler);
}, []);
// Close on Escape
useEffect(() => {
if (!open) return;
const handler = (e: KeyboardEvent) => { if (e.key === "Escape") onClose(); };
window.addEventListener("keydown", handler);
return () => window.removeEventListener("keydown", handler);
}, [open, onClose]);
return (
<div className="flex flex-col h-full bg-bg-card/40 backdrop-blur-md border border-border rounded-xl shadow-lg overflow-hidden">
<div className="px-4 py-3 border-b border-border bg-bg/20">
<span className="flex items-center gap-2 text-sm font-bold text-fg">
<FontAwesomeIcon icon={faHistory} className="text-accent-secondary" />
{t.history}
{entries.length > 0 && (
<span className="text-[10px] bg-accent-primary/20 text-accent-primary px-1.5 py-0.5 rounded-full">
{entries.length}
</span>
)}
</span>
</div>
<>
{/* Backdrop */}
{open && (
<div
className="fixed inset-0 z-40 bg-black/30 backdrop-blur-sm"
onClick={onClose}
aria-hidden
/>
)}
<div className="flex-1 overflow-y-auto scrollbar-thin p-2 space-y-1">
{entries.length === 0 ? (
<p className="px-3 py-6 text-xs text-fg-muted text-center italic">
{t.noHistory}
</p>
) : (
entries.map((e) => (
<HistoryRow
key={e.id}
entry={e}
onLoad={() => onLoad(e)}
onRemove={() => setEntries(removeFromHistory(e.id))}
/>
))
)}
</div>
{entries.length > 0 && (
<div className="p-2 border-t border-border bg-bg/20">
{/* Drawer */}
<div
className={`fixed top-0 right-0 h-full z-50 w-80 flex flex-col bg-bg-card/90 backdrop-blur-2xl border-l border-border shadow-2xl transition-transform duration-300 ease-out ${
open ? "translate-x-0" : "translate-x-full"
}`}
role="dialog"
aria-modal
aria-label="History"
>
{/* Header */}
<div className="flex items-center justify-between px-4 py-3 border-b border-border shrink-0">
<span className="flex items-center gap-2 text-sm font-bold text-fg">
<FontAwesomeIcon icon={faHistory} className="text-accent-primary" />
{t.history}
{entries.length > 0 && (
<span className="text-[10px] bg-accent-primary/15 text-accent-primary px-1.5 py-0.5 rounded-full font-bold">
{entries.length}
</span>
)}
</span>
<button
type="button"
onClick={() => {
clearHistory();
setEntries([]);
}}
className="w-full inline-flex items-center justify-center gap-2 text-xs font-semibold text-fg-muted hover:text-rose-400 hover:bg-rose-400/10 transition-colors py-2 rounded-lg"
onClick={onClose}
className="w-7 h-7 flex items-center justify-center rounded-lg text-fg-muted hover:text-fg hover:bg-bg-hover transition-colors"
aria-label="Close history"
>
<FontAwesomeIcon icon={faTrashAlt} />
{t.history === 'History' ? 'Clear history' : 'Verlauf löschen'}
<FontAwesomeIcon icon={faTimes} />
</button>
</div>
)}
</div>
{/* Entries */}
<div className="flex-1 overflow-y-auto scrollbar-thin p-2 space-y-1">
{entries.length === 0 ? (
<p className="px-3 py-10 text-xs text-fg-muted text-center italic">{t.noHistory}</p>
) : (
entries.map((e) => (
<HistoryRow
key={e.id}
entry={e}
onLoad={() => { onLoad(e); onClose(); }}
onRemove={() => setEntries(removeFromHistory(e.id))}
/>
))
)}
</div>
{/* Footer */}
{entries.length > 0 && (
<div className="p-2 border-t border-border shrink-0">
<button
type="button"
onClick={() => { clearHistory(); setEntries([]); }}
className="w-full inline-flex items-center justify-center gap-2 text-xs font-semibold text-fg-muted hover:text-rose-400 hover:bg-rose-400/10 transition-colors py-2 rounded-lg"
>
<FontAwesomeIcon icon={faTrashAlt} />
{t.history === 'History' ? 'Clear history' : 'Verlauf löschen'}
</button>
</div>
)}
</div>
</>
);
}
// Keep old named export for compatibility but point to the drawer
export { HistoryDrawer as HistoryPanel };
function HistoryRow({
entry,
onLoad,
@@ -93,15 +128,18 @@ function HistoryRow({
}) {
const summary = summarizeInput(entry.input);
return (
<div className="group px-3 py-2.5 flex items-center justify-between gap-2 text-sm hover:bg-bg-hover/80 rounded-lg transition-colors cursor-pointer" onClick={onLoad}>
<div
className="group px-3 py-2.5 flex items-center justify-between gap-2 text-sm hover:bg-bg-hover/80 rounded-xl transition-colors cursor-pointer"
onClick={onLoad}
>
<div className="flex-1 min-w-0">
<div className="truncate text-fg font-medium text-[13px]">{summary}</div>
<div className="flex items-center gap-1.5 text-[10px] text-fg-muted mt-1 uppercase tracking-wider font-semibold">
<div className="truncate text-fg font-semibold text-[13px]">{summary}</div>
<div className="flex items-center gap-1.5 text-[10px] text-fg-muted mt-0.5 uppercase tracking-wider font-semibold">
<FontAwesomeIcon icon={faClock} />
<span>{formatRelative(entry.timestamp)}</span>
{entry.input.mood && (
<>
<span className="opacity-50">·</span>
<span className="opacity-40">·</span>
<span className="truncate">{entry.input.mood}</span>
</>
)}
@@ -110,7 +148,7 @@ function HistoryRow({
<button
type="button"
onClick={(e) => { e.stopPropagation(); onRemove(); }}
className="opacity-0 group-hover:opacity-100 text-fg-muted hover:text-rose-400 transition-all p-1.5 rounded-md hover:bg-rose-400/10"
className="opacity-0 group-hover:opacity-100 text-fg-muted hover:text-rose-400 transition-all p-1.5 rounded-lg hover:bg-rose-400/10"
aria-label="Remove from history"
>
<FontAwesomeIcon icon={faTimes} />
@@ -121,7 +159,7 @@ function HistoryRow({
function summarizeInput(v: InputValues): string {
const idea = v.idea.trim();
const max = 60;
const max = 55;
if (idea.length <= max) return idea || "(no idea text)";
return idea.slice(0, max - 1) + "…";
}