feat(theme): implement single-file CSS theming engine with dark, light, high-contrast, and soft themes
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 16s

This commit is contained in:
sandrodach committed 2026-10-24 23:26:31 +02:00
1 parent 0f24cbdbf4
commit b3e651b3b9
18 files changed
+1455 -804

No files matched your search

+43 -41
View File
@@ -193,19 +193,19 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
switch (priority) {
case 'urgent':
return (
<span className="px-2 py-0.5 text-[11px] font-semibold border border-accent text-accent">
<span className="px-2 py-0.5 text-[11px] font-semibold border border-accent rounded-btn text-accent">
Dringend
</span>
);
case 'high':
return (
<span className="px-2 py-0.5 text-[11px] font-medium border border-white/20 text-zinc-200">
<span className="px-2 py-0.5 text-[11px] font-medium border border-strong rounded-btn text-main">
Wichtig
</span>
);
default:
return (
<span className="px-2 py-0.5 text-[11px] font-medium bg-panel-elevated text-zinc-400 border border-border">
<span className="px-2 py-0.5 text-[11px] font-medium bg-panel-elevated text-muted border border-border rounded-btn">
Quick Dump
</span>
);
@@ -215,14 +215,14 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
const sourceBadge = (source: string) => {
if (source === 'voice') {
return (
<span className="px-2 py-0.5 text-[11px] bg-canvas text-accent border border-border flex items-center gap-1">
<span className="px-2 py-0.5 text-[11px] bg-canvas text-accent border border-border rounded-btn flex items-center gap-1">
<Mic className="w-3 h-3" /> Diktat
</span>
);
}
if (source === 'webhook') {
return (
<span className="px-2 py-0.5 text-[11px] bg-canvas text-zinc-300 border border-border flex items-center gap-1">
<span className="px-2 py-0.5 text-[11px] bg-canvas text-muted border border-border rounded-btn flex items-center gap-1">
<Webhook className="w-3 h-3 text-accent" /> Webhook
</span>
);
@@ -232,13 +232,15 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
if (!activeCard) {
return (
<div className="max-w-xl mx-auto mt-2 p-6 bg-panel border border-border text-center">
<div className="w-11 h-11 bg-accent text-white flex items-center justify-center mx-auto mb-3 shadow-sm">
<div className="max-w-xl mx-auto mt-2 p-6 panel-surface text-center">
<div className="w-11 h-11 bg-accent text-on-accent rounded-btn flex items-center justify-center mx-auto mb-3 shadow-sm">
<Sparkles className="w-5 h-5" />
</div>
<h2 className="text-lg font-bold text-white mb-1">Triage-Deck ist leer</h2>
<p className="text-zinc-400 text-xs sm:text-sm max-w-md mx-auto mb-4">
Alle Quick Dump Notizen sind einsortiert oder verworfen. Tippe oben in die Leiste (Taste <kbd className="px-1.5 py-0.5 bg-canvas border border-border text-zinc-300">C</kbd>), um neue Gedanken reinzurotzen.
<h2 className="text-lg font-bold text-main mb-1">Triage-Deck ist leer</h2>
<p className="text-muted text-xs sm:text-sm max-w-md mx-auto mb-4">
Alle Quick Dump Notizen sind einsortiert oder verworfen. Tippe oben in die Leiste (Taste{' '}
<kbd className="px-1.5 py-0.5 bg-canvas border border-border rounded-btn text-main">C</kbd>
), um neue Gedanken reinzurotzen.
</p>
<div className="flex flex-wrap items-center justify-center gap-2">
@@ -271,8 +273,8 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
<span className="badge-accent">
{cards.length} {cards.length === 1 ? 'Quick Dump' : 'Quick Dumps'} offen
</span>
<span className="hidden sm:inline text-xs text-zinc-600">•</span>
<span className="hidden sm:flex items-center gap-1 text-xs text-zinc-400">
<span className="hidden sm:inline text-xs text-faint">•</span>
<span className="hidden sm:flex items-center gap-1 text-xs text-muted">
<Keyboard className="w-3 h-3" /> ← Wegwerfen • → Aufgabe • D Notiz • Leertaste Später
</span>
</div>
@@ -294,11 +296,11 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
{nextCard && (
<div
key={nextCard.id + '-preview'}
className="absolute inset-x-3 top-3 bottom-0 bg-panel/60 border border-border p-4 opacity-40 scale-[0.97] pointer-events-none flex flex-col justify-between"
className="absolute inset-x-3 top-3 bottom-0 panel-surface p-4 opacity-40 scale-[0.97] pointer-events-none flex flex-col justify-between"
>
<div className="flex items-center gap-2">{priorityBadge(nextCard.priority)}</div>
<p className="text-sm text-zinc-400 line-clamp-2">{nextCard.content}</p>
<div className="text-[11px] text-zinc-500">Nächster Quick Dump…</div>
<p className="text-sm text-muted line-clamp-2">{nextCard.content}</p>
<div className="text-[11px] text-faint">Nächster Quick Dump…</div>
</div>
)}
@@ -321,19 +323,19 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
initial={{ scale: 0.97, opacity: 0, y: 10 }}
animate={{ scale: 1, opacity: 1, y: 0 }}
exit={{ scale: 0.93, opacity: 0 }}
className="relative w-full h-full bg-panel border border-border p-4 sm:p-5 cursor-grab active:cursor-grabbing flex flex-col justify-between overflow-hidden"
className="relative w-full h-full panel-surface p-4 sm:p-5 cursor-grab active:cursor-grabbing flex flex-col justify-between overflow-hidden"
>
{/* Dynamic Swipe Overlays */}
<motion.div
style={{ opacity: keepOpacity }}
className="pointer-events-none absolute top-3.5 right-3.5 px-2.5 py-1 bg-accent/20 border border-accent text-accent font-semibold text-xs tracking-wide uppercase"
className="pointer-events-none absolute top-3.5 right-3.5 px-2.5 py-1 bg-accent-dim border border-accent rounded-btn text-accent font-semibold text-xs tracking-wide uppercase"
>
Als Aufgabe 👉
</motion.div>
<motion.div
style={{ opacity: discardOpacity }}
className="pointer-events-none absolute top-3.5 left-3.5 px-2.5 py-1 bg-white/10 border border-white/30 text-zinc-200 font-semibold text-xs tracking-wide uppercase"
className="pointer-events-none absolute top-3.5 left-3.5 px-2.5 py-1 bg-panel-elevated border border-strong rounded-btn text-main font-semibold text-xs tracking-wide uppercase"
>
👈 Wegwerfen
</motion.div>
@@ -352,12 +354,12 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
setIsEditingCard((prev) => !prev);
}}
title="Quick Dump Text vor dem Zuordnen anpassen"
className="h-6 px-2 bg-canvas hover:bg-panel-hover border border-border text-[11px] text-zinc-400 hover:text-white flex items-center gap-1"
className="h-6 px-2 bg-canvas hover:bg-panel-hover border border-border rounded-btn text-[11px] text-muted hover:text-main flex items-center gap-1"
>
<Edit3 className="w-3 h-3" />
<span>Bearbeiten</span>
</button>
<span className="text-xs text-zinc-400 flex items-center gap-1">
<span className="text-xs text-muted flex items-center gap-1">
<Clock className="w-3 h-3" />
{new Date(activeCard.createdAt).toLocaleString('de-DE', {
day: '2-digit',
@@ -378,7 +380,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
onChange={(e) => setEditDraft(e.target.value)}
rows={3}
autoFocus
className="w-full input-field p-2 text-sm text-white resize-none"
className="w-full input-field p-2 text-sm resize-none"
/>
<div className="flex items-center justify-end gap-1.5">
<button
@@ -398,7 +400,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
</div>
</div>
) : (
<p className="text-base sm:text-xl font-medium text-white leading-relaxed break-words">
<p className="text-base sm:text-xl font-medium text-main leading-relaxed break-words">
{activeCard.content}
</p>
)}
@@ -414,14 +416,14 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
</span>
))
) : (
<span className="text-xs text-zinc-500">
<span className="text-xs text-faint">
Links wischen = Wegwerfen • Rechts wischen = Als Aufgabe sichern
</span>
)}
</div>
{activeCard.dueDate && (
<span className="px-2 py-0.5 bg-canvas text-zinc-300 border border-border text-xs">
<span className="px-2 py-0.5 bg-canvas text-muted border border-border rounded-btn text-xs">
📅 {activeCard.dueDate}
</span>
)}
@@ -432,8 +434,8 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
{/* Convert to Task with Due Date Popover */}
{showDateModal && (
<div className="p-3 bg-panel border border-border flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2 text-xs text-zinc-300">
<div className="p-3 panel-surface flex flex-wrap items-center justify-between gap-3">
<div className="flex items-center gap-2 text-xs text-muted">
<Calendar className="w-4 h-4 text-accent" />
<span>Aufgabe mit Fälligkeitsdatum anlegen:</span>
</div>
@@ -456,8 +458,8 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
{/* Convert to Shared List Item Popover */}
{showListSelector && (
<div className="p-3 bg-panel border border-border">
<p className="text-xs text-zinc-400 mb-2">
<div className="p-3 panel-surface">
<p className="text-xs text-muted mb-2">
Quick Dump direkt einer Liste zuordnen:
</p>
<div className="flex flex-wrap gap-1.5">
@@ -475,7 +477,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
</button>
))
) : (
<span className="text-xs text-zinc-400">Keine Listen vorhanden.</span>
<span className="text-xs text-muted">Keine Listen vorhanden.</span>
)}
</div>
</div>
@@ -489,7 +491,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
title="Wegwerfen (Taste H oder Pfeil links)"
className="btn-action h-8 px-2.5 justify-center"
>
<X className="w-3.5 h-3.5 text-zinc-400" />
<X className="w-3.5 h-3.5 text-muted" />
<span>Wegwerfen</span>
</button>
@@ -502,7 +504,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
cards.length <= 1 ? 'opacity-40 cursor-not-allowed' : ''
}`}
>
<SkipForward className="w-3.5 h-3.5 text-zinc-400" />
<SkipForward className="w-3.5 h-3.5 text-muted" />
<span>Später</span>
</button>
@@ -514,7 +516,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
}}
title="Auf eine geteilte Liste setzen"
className={`btn-action h-8 px-2.5 justify-center ${
showListSelector ? 'border-accent text-accent bg-accent/20' : ''
showListSelector ? 'border-accent text-accent bg-accent-dim' : ''
}`}
>
<ShoppingCart className="w-3.5 h-3.5 text-accent" />
@@ -539,7 +541,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
}}
title="Als Aufgabe mit Fälligkeitsdatum (Taste T)"
className={`btn-action h-8 px-2.5 justify-center ${
showDateModal ? 'border-accent text-accent bg-accent/20' : ''
showDateModal ? 'border-accent text-accent bg-accent-dim' : ''
}`}
>
<Calendar className="w-3.5 h-3.5 text-accent" />
@@ -561,16 +563,16 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
{cards.length > 1 && (
<div className="pt-2">
<div className="flex items-center justify-between mb-1.5 px-0.5">
<span className="text-xs font-semibold uppercase tracking-wider text-zinc-400 flex items-center gap-1.5">
<span className="text-xs font-semibold uppercase tracking-wider text-muted flex items-center gap-1.5">
<Layers className="w-3.5 h-3.5 text-accent" />
Warteschlange ({cards.length - 1} weitere Quick Dumps)
</span>
<span className="text-[11px] text-zinc-500">
<span className="text-[11px] text-faint">
Klicken zum Vorholen oder direkt zuordnen
</span>
</div>
<div className="bg-panel border border-border divide-y divide-border max-h-60 overflow-y-auto">
<div className="panel-surface divide-y divide-border max-h-60 overflow-y-auto">
{cards.slice(1).map((item) => (
<div
key={item.id}
@@ -581,7 +583,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
className="flex-1 min-w-0 cursor-pointer flex items-center gap-2"
>
<span className="w-1.5 h-1.5 bg-accent flex-shrink-0" />
<span className="text-xs text-zinc-200 truncate group-hover:text-white">
<span className="text-xs text-muted truncate group-hover:text-main">
{item.content}
</span>
</div>
@@ -593,7 +595,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
handleAction('keep', { convertTo: 'task', thoughtOverride: item })
}
title="Direkt zur Aufgabe machen"
className="h-6 px-2 bg-canvas hover:bg-accent hover:text-white border border-border text-[11px] text-zinc-300 flex items-center gap-1 transition-colors"
className="h-6 px-2 bg-canvas hover:bg-accent hover:text-on-accent border border-border rounded-btn text-[11px] text-muted flex items-center gap-1 transition-colors"
>
<Check className="w-3 h-3" />
<span className="hidden sm:inline">Aufgabe</span>
@@ -605,7 +607,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
handleAction('keep', { convertTo: 'document', thoughtOverride: item })
}
title="Direkt als Workspace-Notiz anlegen"
className="h-6 px-2 bg-canvas hover:bg-panel-elevated border border-border text-[11px] text-zinc-300 flex items-center gap-1 transition-colors"
className="h-6 px-2 bg-canvas hover:bg-panel-elevated border border-border rounded-btn text-[11px] text-muted hover:text-main flex items-center gap-1 transition-colors"
>
<FileText className="w-3 h-3 text-accent" />
<span className="hidden sm:inline">Notiz</span>
@@ -622,7 +624,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
})
}
title={`Direkt auf Liste "${lists[0].title}" setzen`}
className="h-6 px-2 bg-canvas hover:bg-panel-elevated border border-border text-[11px] text-zinc-300 flex items-center gap-1 transition-colors"
className="h-6 px-2 bg-canvas hover:bg-panel-elevated border border-border rounded-btn text-[11px] text-muted hover:text-main flex items-center gap-1 transition-colors"
>
<ShoppingCart className="w-3 h-3 text-accent" />
<span className="hidden md:inline">{lists[0].title}</span>
@@ -633,7 +635,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
type="button"
onClick={() => handleAction('discard', { thoughtOverride: item })}
title="Wegwerfen"
className="h-6 w-6 bg-canvas hover:bg-panel-elevated border border-border text-zinc-400 hover:text-white flex items-center justify-center transition-colors"
className="h-6 w-6 bg-canvas hover:bg-panel-elevated border border-border rounded-btn text-muted hover:text-main flex items-center justify-center transition-colors"
>
<X className="w-3 h-3" />
</button>