feat(mobile): workspace views in the top menu, taller note rows, equal header bars
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m31s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m31s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
66d6b97aff
commit
21dd5e504e
7 files changed
+99
-50
No files matched your search
+8
-1
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -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",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mindsift-frontend",
|
||||
"private": true,
|
||||
"version": "0.17.1",
|
||||
"version": "0.17.2",
|
||||
"license": "Unlicense",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1022,7 +1022,8 @@ export const SharedListsView: React.FC<SharedListsViewProps> = ({ onRefreshGloba
|
||||
</div>
|
||||
|
||||
{/* Mobile Compact Header Bar (List switching & actions live in MindSift Logo Menu) */}
|
||||
<div className="flex sm:hidden items-center justify-between gap-2 px-3 py-2 rounded-btn bg-panel border border-border !mt-0">
|
||||
{/* Same height with or without the 36 px share button (Tasks, Shopping lists) */}
|
||||
<div className="flex sm:hidden items-center justify-between gap-2 px-3 py-2 min-h-[54px] rounded-btn bg-panel border border-border !mt-0">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<span className="text-sm font-semibold text-main truncate">
|
||||
{activeList ? `${activeList.icon} ${activeList.title}` : '🛒 Einkaufsliste'}
|
||||
|
||||
@@ -1589,7 +1589,8 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
</div>
|
||||
|
||||
{/* Mobile Compact Context Bar (replaces cluttered button rows; options live in MindSift Logo Menu) */}
|
||||
<div className="flex sm:hidden items-center justify-between gap-2 px-3 py-2 rounded-btn bg-panel border border-border !mt-0">
|
||||
{/* Same height with or without the 36 px share button (Tasks, Shopping lists) */}
|
||||
<div className="flex sm:hidden items-center justify-between gap-2 px-3 py-2 min-h-[54px] rounded-btn bg-panel border border-border !mt-0">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<span className="text-sm font-semibold text-main truncate">
|
||||
{currentSelectedList ? `${currentSelectedList.icon} ${currentSelectedList.title}` : '📋 Alle Aufgaben'}
|
||||
|
||||
@@ -141,7 +141,7 @@ const FolderTreeItem: React.FC<FolderTreeItemProps> = ({
|
||||
}}
|
||||
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<FolderTreeItemProps> = ({
|
||||
{...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<WorkspaceViewProps> = ({
|
||||
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: <FilePlus className="w-4 h-4" />,
|
||||
variant: 'accent',
|
||||
onClick: () => handleCreateDocument(''),
|
||||
},
|
||||
{
|
||||
id: 'new-folder',
|
||||
label: 'Neuer Ordner',
|
||||
icon: <FolderPlus className="w-4 h-4" />,
|
||||
onClick: () => setShowNewFolderModal(true),
|
||||
},
|
||||
{
|
||||
id: 'trash',
|
||||
label: 'Papierkorb',
|
||||
icon: <Trash2 className="w-4 h-4" />,
|
||||
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<WorkspaceViewProps> = ({
|
||||
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: <FileText className="w-4 h-4" />,
|
||||
active: subView === 'editor' || subView === 'journal' || subView === 'trash',
|
||||
onClick: () => {
|
||||
setSubView('editor');
|
||||
setMobileShowDetail(false);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'view-todos',
|
||||
label: 'To-Dos',
|
||||
icon: <Check className="w-4 h-4" />,
|
||||
badge: totalOpenTodos || undefined,
|
||||
active: subView === 'todos',
|
||||
onClick: () => {
|
||||
setSubView('todos');
|
||||
setMobileShowDetail(true);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'view-tags',
|
||||
label: 'Hashtags',
|
||||
icon: <Hash className="w-4 h-4" />,
|
||||
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: <FilePlus className="w-4 h-4" />,
|
||||
variant: 'accent',
|
||||
onClick: () => handleCreateDocument(''),
|
||||
},
|
||||
{
|
||||
id: 'new-folder',
|
||||
label: 'Neuer Ordner',
|
||||
icon: <FolderPlus className="w-4 h-4" />,
|
||||
onClick: () => setShowNewFolderModal(true),
|
||||
},
|
||||
{
|
||||
id: 'trash',
|
||||
label: 'Papierkorb',
|
||||
icon: <Trash2 className="w-4 h-4" />,
|
||||
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<WorkspaceViewProps> = ({
|
||||
return (
|
||||
<div className="w-full h-full min-h-0 flex flex-col gap-3">
|
||||
{/* Top Header: Workspace Tabs & Actions */}
|
||||
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-border pb-2">
|
||||
{/* 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 */}
|
||||
<div className="hidden sm:flex flex-wrap items-center justify-between gap-2 border-b border-border pb-2">
|
||||
{/* Workspace selector tabs & left sidebar toggle */}
|
||||
<div className="grid grid-cols-3 w-full gap-1 sm:flex sm:flex-wrap sm:items-center sm:w-auto">
|
||||
<button
|
||||
type="button"
|
||||
@@ -1774,7 +1814,7 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
setSubView('editor');
|
||||
setMobileShowDetail(true);
|
||||
}}
|
||||
className={`w-full flex items-center gap-2 px-2 py-1.5 rounded-btn text-left text-xs cursor-pointer border transition-colors ${
|
||||
className={`w-full flex items-center gap-2 px-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'
|
||||
@@ -1837,7 +1877,7 @@ export const WorkspaceView: React.FC<WorkspaceViewProps> = ({
|
||||
setSubView('editor');
|
||||
setMobileShowDetail(true);
|
||||
}}
|
||||
className={`w-full flex items-center gap-2 px-2 py-1.5 rounded-btn text-left text-xs cursor-pointer border transition-colors ${
|
||||
className={`w-full flex items-center gap-2 px-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'
|
||||
|
||||
Reference in new issue
Block a user