Merge dev into main (v0.15.1)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 26s

This commit is contained in:
sandrodach committed 2026-10-31 09:39:01 +01:00
commit 3cd59b6ffa
6 files changed
+70 -26

No files matched your search

+9 -1
View File
@@ -6,6 +6,13 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
## [Unreleased] ## [Unreleased]
## [0.15.1] - 2026-10-31
### Fixed
- Triage am Handy: Die Warteschlange lässt sich wieder zuklappen („Warteschlange ausblenden“) und fährt beim Auf- und Zuklappen weich auf bzw. zu.
- Triage auf dem Desktop: Notizen, die auf einem anderen Gerät einsortiert oder gelöscht wurden, verschwinden jetzt sofort auch hier (bisher wurde nur der Aufgaben-Tab aktualisiert). Kommt der Tab wieder in den Vordergrund, lädt er außerdem neu, falls Änderungen im Hintergrund verpasst wurden.
- Aufgaben: Ein Tipp irgendwo auf die Aufgabe öffnet sie jetzt zuverlässig, nicht nur auf den Text. Bisher schluckten die Zeile mit Datum/Tags und in Kanban die Pfeil-Leiste jeden Tipp, und am Handy konnte ein etwas längerer Tipp als Ziehen gewertet werden (Ziehen gibt es nur noch mit Maus oder Stift).
## [0.15.0] - 2026-10-31 ## [0.15.0] - 2026-10-31
### Changed ### Changed
@@ -358,7 +365,8 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
- Geteilte Listen (Einkaufs-, Pack- und Checklisten) mit `#Kategorie`-Gruppierung, Mengenangaben und Echtzeit-Abhaken. - Geteilte Listen (Einkaufs-, Pack- und Checklisten) mit `#Kategorie`-Gruppierung, Mengenangaben und Echtzeit-Abhaken.
- Benutzer-, Gruppen- und Admin-Verwaltung (Ersteinrichtungs-Assistent, Einladungscodes, Profilbilder, persönliche API-Keys & Webhook-Ingest `/api/v1/webhooks/ingest`, JSON-Datenexport) sowie CSS-Theme-Engine mit 4 Themes (`Dark`, `Light`, `High Contrast`, `Soft`) und PWA-Unterstützung. - Benutzer-, Gruppen- und Admin-Verwaltung (Ersteinrichtungs-Assistent, Einladungscodes, Profilbilder, persönliche API-Keys & Webhook-Ingest `/api/v1/webhooks/ingest`, JSON-Datenexport) sowie CSS-Theme-Engine mit 4 Themes (`Dark`, `Light`, `High Contrast`, `Soft`) und PWA-Unterstützung.
[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.15.0...dev [Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.15.1...dev
[0.15.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.15.0...v0.15.1
[0.15.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.14.3...v0.15.0 [0.15.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.14.3...v0.15.0
[0.14.3]: https://git.dsit.me/sandrodach/MindSift/compare/v0.14.2...v0.14.3 [0.14.3]: https://git.dsit.me/sandrodach/MindSift/compare/v0.14.2...v0.14.3
[0.14.2]: https://git.dsit.me/sandrodach/MindSift/compare/v0.14.1...v0.14.2 [0.14.2]: https://git.dsit.me/sandrodach/MindSift/compare/v0.14.1...v0.14.2
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "mindsift-frontend", "name": "mindsift-frontend",
"version": "0.15.0", "version": "0.15.1",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mindsift-frontend", "name": "mindsift-frontend",
"version": "0.15.0", "version": "0.15.1",
"license": "Unlicense", "license": "Unlicense",
"dependencies": { "dependencies": {
"@fontsource-variable/inter": "5.3.0", "@fontsource-variable/inter": "5.3.0",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "mindsift-frontend", "name": "mindsift-frontend",
"private": true, "private": true,
"version": "0.15.0", "version": "0.15.1",
"license": "Unlicense", "license": "Unlicense",
"type": "module", "type": "module",
"scripts": { "scripts": {
+18 -2
View File
@@ -628,8 +628,20 @@ export const App: React.FC = () => {
}, 5000); }, 5000);
}, []); }, []);
// Refresh stats on task/doc/list real-time events (counters in the navigation) // Refresh stats on task/doc/list real-time events (counters in the navigation). Task events also
// reload the triage deck: a note sorted on another device must leave this deck too.
useEffect(() => { useEffect(() => {
let deckTimer: number | undefined;
const reloadDeck = () => {
window.clearTimeout(deckTimer);
deckTimer = window.setTimeout(() => {
api.getTriageDeck().then((res) => setDeck(Array.isArray(res) ? res : [])).catch(() => {});
}, 300);
};
// Events missed while the tab slept (desktop in the background, connection dropped)
const onVisible = () => {
if (document.visibilityState === 'visible' && getToken()) refreshAll();
};
const handleWs = (e: any) => { const handleWs = (e: any) => {
const msg = e.detail; const msg = e.detail;
if (!msg) return; if (!msg) return;
@@ -644,13 +656,17 @@ export const App: React.FC = () => {
api.getMe().then((res) => { api.getMe().then((res) => {
setStats(res.stats); setStats(res.stats);
}).catch(() => {}); }).catch(() => {});
if (msg.event.startsWith('TASK_')) reloadDeck();
} }
}; };
window.addEventListener('mindsift:ws', handleWs); window.addEventListener('mindsift:ws', handleWs);
document.addEventListener('visibilitychange', onVisible);
return () => { return () => {
window.clearTimeout(deckTimer);
window.removeEventListener('mindsift:ws', handleWs); window.removeEventListener('mindsift:ws', handleWs);
document.removeEventListener('visibilitychange', onVisible);
}; };
}, []); }, [refreshAll]);
// Close profile dropdown on outside click // Close profile dropdown on outside click
useEffect(() => { useEffect(() => {
+17 -12
View File
@@ -37,6 +37,17 @@ import { CountBadge } from '../../components/CountBadge';
import { FormSheet } from '../../components/FormSheet'; import { FormSheet } from '../../components/FormSheet';
import { useMobileActions } from '../../context/MobileActionContext'; import { useMobileActions } from '../../context/MobileActionContext';
// Drag & drop needs a mouse or pen; on touch-only devices a draggable row can swallow a slightly
// longer tap, so the row would not open
const canDragTasks =
typeof window !== 'undefined' && !!window.matchMedia?.('(any-pointer: fine)').matches;
// Rows inside a task card keep taps on their own controls; taps on empty space or plain labels
// (due date, webhook) reach the card and open the task
const stopOnControl = (e: React.MouseEvent) => {
if ((e.target as HTMLElement).closest('button, select, a, input')) e.stopPropagation();
};
interface TasksViewProps { interface TasksViewProps {
currentUser?: User | null; currentUser?: User | null;
onRefreshStats: () => void; onRefreshStats: () => void;
@@ -1093,7 +1104,7 @@ export const TasksView: React.FC<TasksViewProps> = ({
return ( return (
<div <div
key={task.id} key={task.id}
draggable={!readOnly} draggable={canDragTasks && !readOnly}
onDragStart={(e) => handleTaskDragStart(e, task)} onDragStart={(e) => handleTaskDragStart(e, task)}
onDragEnd={() => { onDragEnd={() => {
setDraggingTaskId(null); setDraggingTaskId(null);
@@ -1143,13 +1154,7 @@ export const TasksView: React.FC<TasksViewProps> = ({
</button> </button>
{/* Clickable Title & Metadata */} {/* Clickable Title & Metadata */}
<div <div className="min-w-0 flex-1 cursor-pointer space-y-1">
onClick={() => {
setDetailTaskId(task.id);
setShowDetailModal(true);
}}
className="min-w-0 flex-1 cursor-pointer space-y-1"
>
<div className="flex items-center gap-2 flex-wrap"> <div className="flex items-center gap-2 flex-wrap">
<p <p
className={`text-sm break-words leading-tight ${ className={`text-sm break-words leading-tight ${
@@ -1166,7 +1171,7 @@ export const TasksView: React.FC<TasksViewProps> = ({
{/* Interactive Badges Bar */} {/* Interactive Badges Bar */}
<div <div
className="flex flex-wrap items-center gap-1.5 pt-0.5 empty:hidden" className="flex flex-wrap items-center gap-1.5 pt-0.5 empty:hidden"
onClick={(e) => e.stopPropagation()} onClick={stopOnControl}
> >
{(task.convertedType === 'document_block' || task.docId) && ( {(task.convertedType === 'document_block' || task.docId) && (
<button <button
@@ -2163,7 +2168,7 @@ export const TasksView: React.FC<TasksViewProps> = ({
return ( return (
<div <div
key={task.id} key={task.id}
draggable={!readOnly} draggable={canDragTasks && !readOnly}
onDragStart={(e) => handleTaskDragStart(e, task)} onDragStart={(e) => handleTaskDragStart(e, task)}
onDragEnd={() => { onDragEnd={() => {
setDraggingTaskId(null); setDraggingTaskId(null);
@@ -2230,7 +2235,7 @@ export const TasksView: React.FC<TasksViewProps> = ({
)} )}
{/* Badges: In Notiz, Priority, Status, Tags */} {/* Badges: In Notiz, Priority, Status, Tags */}
<div className="flex flex-wrap items-center gap-1 pt-0.5" onClick={(e) => e.stopPropagation()}> <div className="flex flex-wrap items-center gap-1 pt-0.5" onClick={stopOnControl}>
{(task.convertedType === 'document_block' || task.docId) && ( {(task.convertedType === 'document_block' || task.docId) && (
<button <button
type="button" type="button"
@@ -2315,7 +2320,7 @@ export const TasksView: React.FC<TasksViewProps> = ({
{!readOnly && ( {!readOnly && (
<div <div
className="flex items-center justify-between pt-1 border-t border-border text-[10px]" className="flex items-center justify-between pt-1 border-t border-border text-[10px]"
onClick={(e) => e.stopPropagation()} onClick={stopOnControl}
> >
<button <button
type="button" type="button"
+23 -8
View File
@@ -916,21 +916,34 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
{cards.length > 1 && ( {cards.length > 1 && (
<div className="pt-2" ref={queueRef}> <div className="pt-2" ref={queueRef}>
{/* Phones: the queue stays folded so the card and its buttons fit on the screen */} {/* Phones: the queue stays folded so the card and its buttons fit on the screen */}
{!showQueueMobile && (
<button <button
type="button" type="button"
aria-expanded={showQueueMobile}
onClick={() => { onClick={() => {
setShowQueueMobile(true); const opening = !showQueueMobile;
window.setTimeout(() => queueRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }), 50); setShowQueueMobile(opening);
if (opening) {
window.setTimeout(() => queueRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' }), 320);
}
}} }}
className="sm:hidden w-full min-h-[48px] px-3 rounded-xl border border-border bg-panel text-sm font-medium text-main flex items-center justify-center gap-2 touch-manipulation cursor-pointer" className="sm:hidden w-full min-h-[48px] px-3 rounded-xl border border-border bg-panel text-sm font-medium text-main flex items-center justify-center gap-2 touch-manipulation cursor-pointer"
> >
<Layers className="w-4 h-4 text-accent" /> <Layers className="w-4 h-4 text-accent" />
<span>Warteschlange anzeigen ({cards.length - 1})</span> <span>
<ChevronDown className="w-4 h-4 text-muted" /> Warteschlange {showQueueMobile ? 'ausblenden' : 'anzeigen'} ({cards.length - 1})
</span>
<ChevronDown
className={`w-4 h-4 text-muted transition-transform duration-300 ${showQueueMobile ? 'rotate-180' : ''}`}
/>
</button> </button>
)} {/* Folds open/closed on phones; always open from sm upwards */}
<div className={`${showQueueMobile ? 'flex' : 'hidden sm:flex'} items-center justify-between mb-2 px-1`}> <div
className={`grid transition-[grid-template-rows,opacity,visibility] duration-300 ease-out sm:grid-rows-[1fr] sm:opacity-100 sm:visible ${
showQueueMobile ? 'grid-rows-[1fr] opacity-100 visible' : 'grid-rows-[0fr] opacity-0 invisible'
}`}
>
<div className="overflow-hidden min-h-0">
<div className="flex items-center justify-between mb-2 px-1 pt-2 sm:pt-0">
<span className="text-xs font-semibold uppercase tracking-wider text-muted 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" /> <Layers className="w-3.5 h-3.5 text-accent" />
Warteschlange ({cards.length - 1}) Warteschlange ({cards.length - 1})
@@ -940,7 +953,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
</span> </span>
</div> </div>
<div className={`${showQueueMobile ? 'block' : 'hidden sm:block'} panel-surface divide-y divide-border max-h-80 overflow-y-auto overflow-x-hidden`}> <div className="panel-surface divide-y divide-border max-h-80 overflow-y-auto overflow-x-hidden">
{cards.slice(1).map((item) => ( {cards.slice(1).map((item) => (
<SwipeableQueueItem <SwipeableQueueItem
key={item.id} key={item.id}
@@ -953,6 +966,8 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
))} ))}
</div> </div>
</div> </div>
</div>
</div>
)} )}
{/* LONG-PRESS / MORE OPTIONS ACTION SHEET (For Triage Card & Queue Items) */} {/* LONG-PRESS / MORE OPTIONS ACTION SHEET (For Triage Card & Queue Items) */}