fix: shift range from a pinned note in its folder; kanban fits the window; list toolbar; quick dump targets only select; aligned bullets; steady search field; browser voice waits 5 s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m28s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
sandrodachandClaude Opus 5.5 committed 2026-11-01 16:12:01 +01:00
1 parent 5a9c5bac37
commit e2b9daba75
26 files changed
+208 -138

No files matched your search

+81 -44
View File
@@ -428,6 +428,35 @@ export const TasksView: React.FC<TasksViewProps> = ({
[ownColumnOf, boardIdOf]
);
// Desktop Kanban in one row: the columns fill the screen height and scroll on their own, so the page
// itself does not scroll as well (like the workspace). Several rows of columns keep the page scroll.
const kanbanBoardRef = useRef<HTMLDivElement>(null);
const [kanbanColHeight, setKanbanColHeight] = useState<number | null>(null);
const kanbanOneRow = viewMode === 'kanban' && (isKanbanWide || boardColumns.length <= 4);
useEffect(() => {
if (!kanbanOneRow) {
setKanbanColHeight(null);
return;
}
const fit = () => {
const el = kanbanBoardRef.current;
if (!el || window.innerWidth < 1024) {
setKanbanColHeight(null);
return;
}
// From the board's top to the window's bottom; then take back what still overflows (footer, paddings)
const top = el.getBoundingClientRect().top + window.scrollY;
setKanbanColHeight(Math.max(320, window.innerHeight - top - 16));
requestAnimationFrame(() => {
const over = document.documentElement.scrollHeight - window.innerHeight;
if (over > 0) setKanbanColHeight((h) => (h ? Math.max(320, h - over) : h));
});
};
fit();
window.addEventListener('resize', fit);
return () => window.removeEventListener('resize', fit);
}, [kanbanOneRow, selectedListId, loading]);
const safeTasks = useMemo(() => {
if (!Array.isArray(tasks)) return [];
const seen = new Set<string>();
@@ -1976,9 +2005,9 @@ export const TasksView: React.FC<TasksViewProps> = ({
})}
</div>
{/* Group By & Sort By Dropdowns & Manage Columns button */}
{/* Kanban: manage columns and width (the list view has grouping and sorting above the list) */}
<div className="flex items-center gap-2 flex-shrink-0 ml-auto">
{viewMode === 'kanban' ? (
{viewMode === 'kanban' && (
<>
{!isSelectedListReadOnly && (
<button
@@ -2004,40 +2033,6 @@ export const TasksView: React.FC<TasksViewProps> = ({
)}
</button>
</>
) : (
<>
{/* Group By */}
<div className="flex items-center gap-1 text-xs text-muted">
<span className="text-[11px]">Gruppe:</span>
<select
value={groupBy}
onChange={(e) => setGroupBy(e.target.value as any)}
className="h-7 px-2 input-field text-xs bg-panel"
>
<option value="none">Keine</option>
<option value="tag">Nach Hashtag</option>
<option value="status">Nach Status</option>
<option value="priority">Nach Priorität</option>
<option value="due">Nach Fälligkeit</option>
</select>
</div>
{/* Sort By */}
<div className="flex items-center gap-1 text-xs text-muted">
<span className="text-[11px]">Sortierung:</span>
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value as any)}
className="h-7 px-2 input-field text-xs bg-panel"
>
<option value="custom">Manuell (Drag & Drop)</option>
<option value="created">Erstelldatum</option>
<option value="due">Fälligkeitsdatum</option>
<option value="priority">Priorität</option>
<option value="alpha">Titel (A-Z)</option>
</select>
</div>
</>
)}
</div>
</div>
@@ -2464,6 +2459,7 @@ export const TasksView: React.FC<TasksViewProps> = ({
<div className="space-y-2">
{kanbanTouch.ghost}
<div
ref={kanbanBoardRef}
className={`grid grid-cols-1 md:grid-cols-2 gap-3 pt-1 lg:w-[var(--kanban-w)] lg:ml-[var(--kanban-ml)] transition-[width,margin] duration-300 ease-out ${
isKanbanWide ? 'lg:grid-cols-[repeat(var(--kanban-cols),minmax(0,1fr))]' : 'lg:grid-cols-4'
}`}
@@ -2486,9 +2482,10 @@ export const TasksView: React.FC<TasksViewProps> = ({
onDragLeave={(e) => handleKanbanColDragLeave(e, col.id)}
onDrop={(e) => handleKanbanDrop(e, col.id, colTasks)}
data-kanban-col={col.id}
className={`bg-canvas border border-border flex flex-col min-h-[420px] overflow-hidden transition-all ${
dragOverKanbanCol === col.id ? 'ring-2 ring-accent/60 bg-accent/5' : ''
} ${blockedForDrag ? 'opacity-40' : ''}`}
style={kanbanColHeight ? { height: kanbanColHeight } : undefined}
className={`bg-canvas border border-border flex flex-col overflow-hidden transition-all ${
kanbanColHeight ? '' : 'min-h-[420px]'
} ${dragOverKanbanCol === col.id ? 'ring-2 ring-accent/60 bg-accent/5' : ''} ${blockedForDrag ? 'opacity-40' : ''}`}
title={blockedForDrag ? 'Diese Spalte gibt es in der Liste der Aufgabe nicht' : undefined}
>
{/* Column Header */}
@@ -2501,7 +2498,7 @@ export const TasksView: React.FC<TasksViewProps> = ({
</div>
{/* Tasks List in Column */}
<div className="p-2 space-y-2 flex-1 overflow-y-auto max-h-[70vh]">
<div className={`p-2 space-y-2 flex-1 min-h-0 overflow-y-auto ${kanbanColHeight ? '' : 'max-h-[70vh]'}`}>
{colTasks.map((task) => {
const isDragging = draggingTaskId === task.id;
const readOnly = isTaskReadOnly(task);
@@ -2748,16 +2745,56 @@ export const TasksView: React.FC<TasksViewProps> = ({
</button>
</div>
{/* Grouping and sorting right above the list, then clearing as a plain trash icon (desktop; phones
use the menu) */}
<div className="hidden sm:flex items-center gap-2 ml-auto">
<div className="flex items-center gap-1 text-xs text-muted">
<span className="text-[11px]">Gruppe:</span>
<select
value={groupBy}
onChange={(e) => setGroupBy(e.target.value as any)}
className="h-8 px-2 input-field text-xs bg-panel"
>
<option value="none">Keine</option>
<option value="tag">Nach Hashtag</option>
<option value="status">Nach Status</option>
<option value="priority">Nach Priorität</option>
<option value="due">Nach Fälligkeit</option>
</select>
</div>
<div className="flex items-center gap-1 text-xs text-muted">
<span className="text-[11px]">Sortierung:</span>
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value as any)}
className="h-8 px-2 input-field text-xs bg-panel"
>
<option value="custom">Manuell (Drag & Drop)</option>
<option value="created">Erstelldatum</option>
<option value="due">Fälligkeitsdatum</option>
<option value="priority">Priorität</option>
<option value="alpha">Titel (A-Z)</option>
</select>
</div>
</div>
{filter === 'discarded' && taskTrash.some((t) => t.canEdit) && (
<button onClick={handleEmptyTaskTrash} className="btn-action min-h-[38px] sm:min-h-0 ml-auto">
<button
onClick={handleEmptyTaskTrash}
className="btn-action h-8 w-8 p-0 justify-center min-h-[38px] min-w-[38px] sm:min-h-0 sm:min-w-0 ml-auto sm:ml-0"
title="Papierkorb leeren"
aria-label="Papierkorb leeren"
>
<Trash2 className="w-3.5 h-3.5" />
Papierkorb leeren
</button>
)}
{filter === 'done' && doneTasks.length > 0 && !isSelectedListReadOnly && (
<button onClick={handleClearDone} className="btn-action min-h-[38px] sm:min-h-0 ml-auto">
<button
onClick={handleClearDone}
className="btn-action h-8 w-8 p-0 justify-center min-h-[38px] min-w-[38px] sm:min-h-0 sm:min-w-0 ml-auto sm:ml-0"
title="Erledigte in den Papierkorb"
aria-label="Erledigte in den Papierkorb"
>
<Trash2 className="w-3.5 h-3.5" />
Erledigte leeren
</button>
)}
</div>