diff --git a/CHANGELOG.md b/CHANGELOG.md
index f468745..24ce2ce 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,19 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
## [Unreleased]
+## [0.17.0] - 2026-10-31
+
+### Added
+- Aufgaben und Kanban: Eine Aufgabe lässt sich auf den Reiter einer anderen Aufgabenliste ziehen und landet dort wie eine neue Aufgabe. Ihren Kanban-Status behält sie, wenn die Zielliste diese Spalte hat, sonst startet sie in der ersten Spalte. Nur lesbare Listen nehmen nichts an.
+- Einkaufslisten: Einträge lassen sich genauso auf den Reiter einer anderen Liste ziehen; Kategorie, Menge und Haken bleiben erhalten. Neu dafür ist `POST /api/v1/lists/{id}/items/{itemId}/move` (Bearbeitungsrecht in beiden Listen nötig).
+- Aufgaben und Einkaufslisten merken sich beim Wechsel zwischen den Reitern der App die offene Liste, Aufgaben außerdem Liste oder Kanban (pro Browser-Tab, wie der Workspace).
+
+### Changed
+- Einkaufslisten: Die Verbindungsanzeige oben rechts sieht jetzt aus wie bei den Notizen (Punkt und „Live“ bzw. „Offline“) statt des alten Funk-Symbols.
+
+### Fixed
+- Push-Benachrichtigungen am Handy: In der Statusleiste erschien statt des Gehirn-Symbols nur ein einfarbiges Quadrat. Android nutzt dort nur die Transparenz des Bildes; MindSift liefert dafür jetzt ein eigenes Symbol (weißes Gehirn auf transparentem Grund). Die App-Caches werden einmal erneuert.
+
## [0.16.1] - 2026-10-31
### Changed
@@ -382,7 +395,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.
- 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.16.1...dev
+[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.17.0...dev
+[0.17.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.16.1...v0.17.0
[0.16.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.16.0...v0.16.1
[0.16.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.15.1...v0.16.0
[0.15.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.15.0...v0.15.1
diff --git a/Doku/api/rest.md b/Doku/api/rest.md
index 849c414..555fc37 100644
--- a/Doku/api/rest.md
+++ b/Doku/api/rest.md
@@ -190,6 +190,7 @@ Wiederholte Operationen (gleiche `opId`) werden nicht erneut angewendet; die Ant
| PATCH | `/lists/{id}/items/{itemId}` | Abhaken (`isCompleted`) |
| PUT | `/lists/{id}/items/{itemId}` | Ändern (`content`, `category`, `quantity`) |
| DELETE | `/lists/{id}/items/{itemId}` | Löschen |
+| POST | `/lists/{id}/items/{itemId}/move` | In eine andere Liste verschieben (`targetListId`); Bearbeitungsrecht in beiden Listen nötig, der Eintrag landet oben |
| POST/PUT | `/lists/{id}/items/reorder` | Reihenfolge/Kategorie (`items[{id, sortOrder, category, content}]`) |
| POST | `/lists/{id}/clear-completed` | Erledigte entfernen |
diff --git a/README.md b/README.md
index feae067..c3e05a2 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
[](#)
[](#-schnellstart)
[](#-deine-daten-gehören-dir)
-[](CHANGELOG.md)
+[](CHANGELOG.md)
**MindSift** ist eine selbst gehostete Schaltstelle für spontane Gedanken, Aufgaben mit Kanban-Board, hierarchische Outliner-Notizen, die mehrere Personen gleichzeitig bearbeiten können, und gemeinsame Listen. Alles läuft als ein einzelner Container mit eingebetteter SQLite-Datenbank, auf deinem eigenen Server.
@@ -52,7 +52,7 @@ Die meisten Notiz- und Aufgaben-Apps verlangen Struktur im falschen Moment: Wenn
* Listen- und Kanban-Ansicht, Spalten pro Aufgabenliste frei konfigurierbar.
* Prioritäten, Fälligkeitsdatum, Hashtags, Unteraufgaben mit Fortschrittsbalken, Markdown-Notizen.
-* Filter, Gruppierung und Sortierung; Drag & Drop in Liste und Board.
+* Filter, Gruppierung und Sortierung; Drag & Drop in Liste und Board, auch auf den Reiter einer anderen Aufgabenliste.
* **Aufgabenlisten teilen** mit Benutzern oder Gruppen, wahlweise als **Bearbeiter** oder **nur lesend**.
* **Aufgaben beobachten:** Beliebige Aufgaben per Klick abonnieren, um über alle Änderungen informiert zu werden.
* **Wiederholungen:** Wiederkehrende Aufgaben (täglich, wöchentlich, monatlich, jährlich) mit automatischer Folgeaufgabe beim Abhaken.
@@ -97,7 +97,7 @@ Technik dahinter: [Doku/modules/collaboration.md](Doku/modules/collaboration.md)
### 🛒 Geteilte Listen
-* Einkaufs-, Pack- und Checklisten mit Kategorien per Hashtag (`Hafermilch #Kühlregal`), Mengenangaben und Drag & Drop zwischen Kategorien.
+* Einkaufs-, Pack- und Checklisten mit Kategorien per Hashtag (`Hafermilch #Kühlregal`), Mengenangaben und Drag & Drop zwischen Kategorien oder in eine andere Liste.
* Freigabe an Benutzer und Gruppen (Bearbeiter oder Leser); abgehakte Einträge erscheinen bei allen in Echtzeit.
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index f0e846a..31d9b23 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "mindsift-frontend",
- "version": "0.16.1",
+ "version": "0.17.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "mindsift-frontend",
- "version": "0.16.1",
+ "version": "0.17.0",
"license": "Unlicense",
"dependencies": {
"@fontsource-variable/inter": "5.3.0",
diff --git a/frontend/package.json b/frontend/package.json
index d1562a6..a57abc3 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,7 +1,7 @@
{
"name": "mindsift-frontend",
"private": true,
- "version": "0.16.1",
+ "version": "0.17.0",
"license": "Unlicense",
"type": "module",
"scripts": {
diff --git a/frontend/public/badge-96x96.png b/frontend/public/badge-96x96.png
new file mode 100644
index 0000000..8a4b8cd
Binary files /dev/null and b/frontend/public/badge-96x96.png differ
diff --git a/frontend/public/badge.svg b/frontend/public/badge.svg
new file mode 100644
index 0000000..1b38cbf
--- /dev/null
+++ b/frontend/public/badge.svg
@@ -0,0 +1,14 @@
+
diff --git a/frontend/public/sw.js b/frontend/public/sw.js
index f05efa3..23ecc51 100644
--- a/frontend/public/sw.js
+++ b/frontend/public/sw.js
@@ -7,7 +7,7 @@
// would hand it the old values), the cache only offline.
// - API requests are never touched; offline data comes from the app's own cache (lib/offline.ts).
// Raise VERSION when this file changes in a way that needs fresh caches.
-const VERSION = 'v4';
+const VERSION = 'v5';
const SHELL_CACHE = `mindsift-shell-${VERSION}`;
const ASSET_CACHE = `mindsift-assets-${VERSION}`;
const MAX_ASSETS = 80;
@@ -22,6 +22,7 @@ const SHELL_URLS = [
'/icon-192x192.png',
'/icon-512x512.png',
'/apple-touch-icon.png',
+ '/badge-96x96.png',
];
self.addEventListener('install', (event) => {
@@ -194,7 +195,8 @@ self.addEventListener('push', (event) => {
self.registration.showNotification(msg.title || 'MindSift', {
body: msg.body || '',
icon: '/icon-192x192.png',
- badge: '/icon-192x192.png',
+ // Status bar icon: Android only uses the alpha channel, the full app icon became a plain square
+ badge: '/badge-96x96.png',
tag: msg.tag || undefined,
data: { url: msg.url || '/' },
})
diff --git a/frontend/src/features/lists/SharedListsView.tsx b/frontend/src/features/lists/SharedListsView.tsx
index f9793a0..fd795b7 100644
--- a/frontend/src/features/lists/SharedListsView.tsx
+++ b/frontend/src/features/lists/SharedListsView.tsx
@@ -4,13 +4,13 @@ import {
Trash2,
Users,
Check,
- Radio,
Sparkles,
X,
ShoppingCart,
} from 'lucide-react';
import { ListItem, SharedList, Team, User, WSMessage } from '../../lib/types';
import { api, triggerHaptic, createRealtimeConnection, RealtimeTransport } from '../../lib/api';
+import { readViewSession, writeViewSession } from '../../lib/viewSession';
import { UnifiedShareModal } from '../../components/UnifiedShareModal';
import { AvatarStack, AvatarPerson } from '../../components/AvatarStack';
import { CountBadge } from '../../components/CountBadge';
@@ -83,7 +83,13 @@ export function deduplicateItems(items: ListItem[]): ListItem[] {
export const SharedListsView: React.FC = ({ onRefreshGlobal, currentUser, initialListId }) => {
const { setGroups, setPrimaryAction } = useMobileActions();
const [lists, setLists] = useState([]);
- const [activeListId, setActiveListId] = useState(initialListId || '');
+ // The open list survives switching to other tabs (like the workspace's open note)
+ const [activeListId, setActiveListId] = useState(
+ () => initialListId || readViewSession<{ listId: string }>('lists').listId || ''
+ );
+ useEffect(() => {
+ if (activeListId) writeViewSession('lists', { listId: activeListId });
+ }, [activeListId]);
useEffect(() => {
if (initialListId) {
@@ -116,6 +122,8 @@ export const SharedListsView: React.FC = ({ onRefreshGloba
const [draggingItemId, setDraggingItemId] = useState(null);
const [dragOverItemTarget, setDragOverItemTarget] = useState<{ itemId: string; position: 'before' | 'after' } | null>(null);
const [dragOverCategoryTarget, setDragOverCategoryTarget] = useState(null);
+ // List tab a dragged entry hovers over (dropping moves it into that list)
+ const [dragOverListTab, setDragOverListTab] = useState(null);
const realtimeConnRef = useRef(null);
@@ -378,6 +386,35 @@ export const SharedListsView: React.FC = ({ onRefreshGloba
setDraggingItemId(item.id);
};
+ const canWriteList = (lst: SharedList) => lst.isOwner || lst.role !== 'viewer';
+
+ // An entry dragged onto another list's tab lands at the top of that list, like a new entry
+ const handleDropOnListTab = async (e: React.DragEvent, target: SharedList) => {
+ e.preventDefault();
+ const itemId = e.dataTransfer.getData('application/mindsift-list-item-id') || draggingItemId;
+ setDraggingItemId(null);
+ setDragOverItemTarget(null);
+ setDragOverCategoryTarget(null);
+ setDragOverListTab(null);
+ const source = activeList;
+ const item = source?.items.find((i) => i.id === itemId);
+ if (!source || !item || target.id === source.id || !canWriteList(target)) return;
+ triggerHaptic(15);
+ setLists((prev) =>
+ prev.map((l) => {
+ if (l.id === source.id) return { ...l, items: l.items.filter((i) => i.id !== item.id) };
+ if (l.id === target.id) return { ...l, items: deduplicateItems([{ ...item, listId: target.id }, ...l.items]) };
+ return l;
+ })
+ );
+ try {
+ await api.moveListItem(source.id, item.id, target.id);
+ } catch {
+ loadLists();
+ }
+ onRefreshGlobal?.();
+ };
+
const handleItemDragOver = (e: React.DragEvent, targetItemId: string) => {
e.preventDefault();
e.stopPropagation();
@@ -849,6 +886,23 @@ export const SharedListsView: React.FC = ({ onRefreshGloba
]);
}, [lists, activeList, completedItems.length, setGroups, setPrimaryAction]);
+ // Connection pill in the same look as the notes' header
+ const livePill = (
+
+
+ {wsConnected ? 'Live' : 'Offline'}
+
+ );
+
return (