From 21dd5e504e4471b701bc403ef2129bf398771866 Mon Sep 17 00:00:00 2001 From: Sandro Date: Sat, 31 Oct 2026 10:21:08 +0100 Subject: [PATCH] feat(mobile): workspace views in the top menu, taller note rows, equal header bars Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 9 +- README.md | 2 +- frontend/package-lock.json | 4 +- frontend/package.json | 2 +- .../src/features/lists/SharedListsView.tsx | 3 +- frontend/src/features/triage/TasksView.tsx | 3 +- .../src/features/workspace/WorkspaceView.tsx | 126 ++++++++++++------ 7 files changed, 99 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4df762a..4d7fd6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V ## [Unreleased] +## [0.17.2] - 2026-10-31 + +### Changed +- Workspace am Handy: Notizen, To-Dos und Hashtags wechselt man jetzt wie bei Aufgaben und Einkaufslisten über das Menü oben (Logo); die Reiterzeile entfällt dort. Ordner und Notizen in der Liste sind so hoch wie das Suchfeld (44 px) und damit leichter zu treffen. +- Aufgaben am Handy: Die Kopfleiste ist bei „Alle Aufgaben“ genauso hoch wie bei einzelnen Listen und wie bei den Einkaufslisten. + ## [0.17.1] - 2026-10-31 ### Changed @@ -407,7 +413,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.17.1...dev +[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.17.2...dev +[0.17.2]: https://git.dsit.me/sandrodach/MindSift/compare/v0.17.1...v0.17.2 [0.17.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.17.0...v0.17.1 [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 diff --git a/README.md b/README.md index 31d93e8..03da5d2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Self-Hosted](https://img.shields.io/badge/100%25-Self--Hosted-2563eb?style=flat-square)](#) [![Docker](https://img.shields.io/badge/Deployment-Single%20Container-2496ed?style=flat-square&logo=docker&logoColor=white)](#-schnellstart) [![SQLite](https://img.shields.io/badge/Daten-SQLite-003b57?style=flat-square&logo=sqlite&logoColor=white)](#-deine-daten-gehören-dir) -[![Version](https://img.shields.io/badge/Version-v0.17.1-16a34a?style=flat-square)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/Version-v0.17.2-16a34a?style=flat-square)](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. diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9945125..d0a77e5 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "mindsift-frontend", - "version": "0.17.1", + "version": "0.17.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mindsift-frontend", - "version": "0.17.1", + "version": "0.17.2", "license": "Unlicense", "dependencies": { "@fontsource-variable/inter": "5.3.0", diff --git a/frontend/package.json b/frontend/package.json index be8ce34..d20c8e0 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "mindsift-frontend", "private": true, - "version": "0.17.1", + "version": "0.17.2", "license": "Unlicense", "type": "module", "scripts": { diff --git a/frontend/src/features/lists/SharedListsView.tsx b/frontend/src/features/lists/SharedListsView.tsx index fd795b7..6808609 100644 --- a/frontend/src/features/lists/SharedListsView.tsx +++ b/frontend/src/features/lists/SharedListsView.tsx @@ -1022,7 +1022,8 @@ export const SharedListsView: React.FC = ({ onRefreshGloba {/* Mobile Compact Header Bar (List switching & actions live in MindSift Logo Menu) */} -
+ {/* Same height with or without the 36 px share button (Tasks, Shopping lists) */} +
{activeList ? `${activeList.icon} ${activeList.title}` : '🛒 Einkaufsliste'} diff --git a/frontend/src/features/triage/TasksView.tsx b/frontend/src/features/triage/TasksView.tsx index 77d425c..270bd13 100644 --- a/frontend/src/features/triage/TasksView.tsx +++ b/frontend/src/features/triage/TasksView.tsx @@ -1589,7 +1589,8 @@ export const TasksView: React.FC = ({
{/* Mobile Compact Context Bar (replaces cluttered button rows; options live in MindSift Logo Menu) */} -
+ {/* Same height with or without the 36 px share button (Tasks, Shopping lists) */} +
{currentSelectedList ? `${currentSelectedList.icon} ${currentSelectedList.title}` : '📋 Alle Aufgaben'} diff --git a/frontend/src/features/workspace/WorkspaceView.tsx b/frontend/src/features/workspace/WorkspaceView.tsx index 4b5d317..08c5fbf 100644 --- a/frontend/src/features/workspace/WorkspaceView.tsx +++ b/frontend/src/features/workspace/WorkspaceView.tsx @@ -141,7 +141,7 @@ const FolderTreeItem: React.FC = ({ }} onDrop={(e) => handleDropOnFolder(node.fullPath, e)} style={{ paddingLeft: `${depth * 12 + 8}px` }} - className={`group flex items-center justify-between pr-1.5 h-7 rounded-btn text-xs cursor-pointer border-l-2 transition-colors ${ + className={`group flex items-center justify-between pr-1.5 h-11 sm:h-7 rounded-btn text-sm sm:text-xs cursor-pointer border-l-2 transition-colors ${ isDragOver ? 'bg-accent/20 border-accent text-main font-semibold' : 'border-transparent hover:bg-panel-elevated text-muted' @@ -262,7 +262,7 @@ const FolderTreeItem: React.FC = ({ {...docDropProps(d.id)} onClick={() => onSelectDoc(d.id)} style={{ paddingLeft: `${(depth + 1) * 12 + 16}px` }} - className={`w-full flex items-center gap-2 pr-2 py-1.5 rounded-btn text-left text-xs cursor-pointer border transition-colors ${ + className={`w-full flex items-center gap-2 pr-2 py-1.5 min-h-11 sm:min-h-0 rounded-btn text-left text-sm sm:text-xs cursor-pointer border transition-colors ${ mergeHoverId === d.id ? 'border-accent bg-accent/15 ring-2 ring-accent/30 text-main' : selectedDocId === d.id && subView === 'editor' @@ -654,43 +654,6 @@ export const WorkspaceView: React.FC = ({ setMobileShowDetail(true); }, [reloadTrash]); - // Register mobile FAB actions - useEffect(() => { - setPrimaryAction(() => handleCreateDocument('')); - setGroups([ - { - id: 'workspace-actions', - title: 'Notizen & Ordner', - items: [ - { - id: 'new-note', - label: 'Neue Notiz', - icon: , - variant: 'accent', - onClick: () => handleCreateDocument(''), - }, - { - id: 'new-folder', - label: 'Neuer Ordner', - icon: , - onClick: () => setShowNewFolderModal(true), - }, - { - id: 'trash', - label: 'Papierkorb', - icon: , - badge: trash.length, - onClick: openTrash, - }, - ], - }, - ]); - return () => { - setGroups([]); - setPrimaryAction(undefined); - }; - }, [handleCreateDocument, setGroups, setPrimaryAction, trash.length, openTrash]); - const handleDeleteDocument = useCallback( async (docId: string) => { try { @@ -1176,6 +1139,82 @@ export const WorkspaceView: React.FC = ({ return count; }, [docs]); + // Register mobile FAB actions and the menu at the top; on phones the menu also switches between + // notes, to-dos and hashtags (like the lists in Tasks and Shopping lists), the tab row is hidden there + useEffect(() => { + setPrimaryAction(() => handleCreateDocument('')); + setGroups([ + { + id: 'workspace-views', + title: 'Ansicht', + items: [ + { + id: 'view-notes', + label: 'Notizen', + icon: , + active: subView === 'editor' || subView === 'journal' || subView === 'trash', + onClick: () => { + setSubView('editor'); + setMobileShowDetail(false); + }, + }, + { + id: 'view-todos', + label: 'To-Dos', + icon: , + badge: totalOpenTodos || undefined, + active: subView === 'todos', + onClick: () => { + setSubView('todos'); + setMobileShowDetail(true); + }, + }, + { + id: 'view-tags', + label: 'Hashtags', + icon: , + badge: tagsSummary.length || undefined, + active: subView === 'tags' || subView === 'tag_detail', + onClick: () => { + setSubView('tags'); + setMobileShowDetail(true); + }, + }, + ], + }, + { + id: 'workspace-actions', + title: 'Notizen & Ordner', + items: [ + { + id: 'new-note', + label: 'Neue Notiz', + icon: , + variant: 'accent', + onClick: () => handleCreateDocument(''), + }, + { + id: 'new-folder', + label: 'Neuer Ordner', + icon: , + onClick: () => setShowNewFolderModal(true), + }, + { + id: 'trash', + label: 'Papierkorb', + icon: , + badge: trash.length, + onClick: openTrash, + }, + ], + }, + ]); + return () => { + setGroups([]); + setPrimaryAction(undefined); + }; + }, [handleCreateDocument, setGroups, setPrimaryAction, trash.length, openTrash, subView, totalOpenTodos, tagsSummary.length]); + // Real-time matched blocks and all nested children for the selected hashtag const activeTagGroups = useMemo(() => { if (!selectedTag) return []; @@ -1355,8 +1394,9 @@ export const WorkspaceView: React.FC = ({ return (
{/* Top Header: Workspace Tabs & Actions */} -
- {/* Workspace selector tabs & left sidebar toggle; on phones the three tabs share the full width */} + {/* Phones: the views are switched in the menu at the top, so the row is hidden there */} +
+ {/* Workspace selector tabs & left sidebar toggle */}