From 92993c2dd2263bc11b3c3cba145afef74d528ad7 Mon Sep 17 00:00:00 2001 From: Sandro Date: Mon, 2 Nov 2026 12:59:16 +0100 Subject: [PATCH 1/5] style(ui): counters on labels sit right after the word The counter hung at the label's right edge with a fixed 12 px gap, so two-digit numbers covered part of the last letter. It now follows the word in the text flow (position="text"): only its ring overlaps, the same for any number of digits, and wide numbers widen the tab. Co-Authored-By: Claude Opus 5.5 --- frontend/src/App.tsx | 12 ++++++------ frontend/src/components/CountBadge.tsx | 8 +++++--- frontend/src/features/lists/SharedListsView.tsx | 4 ++-- frontend/src/features/triage/TasksView.tsx | 8 ++++---- frontend/src/features/workspace/WorkspaceView.tsx | 8 ++++---- frontend/src/index.css | 8 ++++++++ 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 5b5416b..36fad85 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1198,9 +1198,9 @@ export const App: React.FC = () => { className={`tab-btn ${activeTab === 'triage' ? 'tab-btn-active' : ''}`} > - 0 ? 'pr-3' : ''}`}> + Triage - + @@ -1223,9 +1223,9 @@ export const App: React.FC = () => { className={`tab-btn ${activeTab === 'tasks' ? 'tab-btn-active' : ''}`} > - 0 ? 'pr-3' : ''}`}> + Aufgaben - + @@ -1237,9 +1237,9 @@ export const App: React.FC = () => { className={`tab-btn ${activeTab === 'lists' ? 'tab-btn-active' : ''}`} > - 0 ? 'pr-3' : ''}`}> + Listen - + diff --git a/frontend/src/components/CountBadge.tsx b/frontend/src/components/CountBadge.tsx index f512dde..b411566 100644 --- a/frontend/src/components/CountBadge.tsx +++ b/frontend/src/components/CountBadge.tsx @@ -4,14 +4,16 @@ interface CountBadgeProps { count: number | undefined | null; // accent: something waits for you (nav bar); muted: plain amount (tabs, lists) variant?: 'accent' | 'muted'; - // corner: notification badge on the top right of the nearest `relative` parent - position?: 'inline' | 'corner'; + // corner: notification badge on the top right of the nearest `relative` parent (icons); + // text: right after a label, raised and hanging a little over its last letter (tabs, nav bar) + position?: 'inline' | 'corner' | 'text'; showZero?: boolean; className?: string; // e.g. to fine-tune the corner offset } // Full class names: Tailwind drops component classes it cannot find literally in the source const VARIANT_CLASS = { accent: 'count-badge-accent', muted: 'count-badge-muted' }; +const POSITION_CLASS = { inline: '', corner: 'count-badge-corner', text: 'count-badge-text' }; // The one counter of the app (Doku/ui-patterns.md); hides itself at 0 unless showZero export const CountBadge: React.FC = ({ @@ -25,7 +27,7 @@ export const CountBadge: React.FC = ({ const label = count > 99 ? '99+' : String(count); return ( {label} diff --git a/frontend/src/features/lists/SharedListsView.tsx b/frontend/src/features/lists/SharedListsView.tsx index 88752d8..d09e1dc 100644 --- a/frontend/src/features/lists/SharedListsView.tsx +++ b/frontend/src/features/lists/SharedListsView.tsx @@ -1063,9 +1063,9 @@ export const SharedListsView: React.FC = ({ onRefreshGloba > {lst.icon} {/* Counter on the label, like in the nav bar */} - 0 ? 'pr-3' : ''}`}> + {lst.title} - + = ({ }`} > {/* Counter on the label, like in the nav bar */} - + Alle Aufgaben - + @@ -1825,9 +1825,9 @@ export const TasksView: React.FC = ({ className="h-full px-3 text-xs font-medium flex items-center gap-1.5 rounded-btn" > {tl.icon} - + {tl.title} - + {(() => { diff --git a/frontend/src/features/workspace/WorkspaceView.tsx b/frontend/src/features/workspace/WorkspaceView.tsx index 9708483..fa478bf 100644 --- a/frontend/src/features/workspace/WorkspaceView.tsx +++ b/frontend/src/features/workspace/WorkspaceView.tsx @@ -1623,9 +1623,9 @@ export const WorkspaceView: React.FC = ({ }`} > - 0 ? 'pr-3' : ''}`}> + To-Dos - + @@ -1642,9 +1642,9 @@ export const WorkspaceView: React.FC = ({ }`} > - 0 ? 'pr-3' : ''}`}> + Hashtags - + diff --git a/frontend/src/index.css b/frontend/src/index.css index c1c62f2..7d2f5d0 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -339,6 +339,14 @@ body { /* ring in the page colour keeps the badge readable on top of borders */ box-shadow: 0 0 0 2px var(--bg-app); } + /* In the text flow right after a label: only the ring hangs over the last letter (2 px), however many + digits; wider numbers make the tab wider instead of covering more of the word */ + .count-badge-text { + position: relative; + top: -0.375rem; + z-index: 1; + box-shadow: 0 0 0 2px var(--bg-app); + } .badge-ok { display: inline-flex; From b04991ee2348edcff06d13b9c259c05d0565a6ec Mon Sep 17 00:00:00 2001 From: Sandro Date: Mon, 2 Nov 2026 12:59:16 +0100 Subject: [PATCH 2/5] fix(tasks): full-size fields in the task details on phones Task list, people search, hashtag and subtask fields were 24-32 px with 12 px text on phones; they now match the other fields (44 px, 16 px text, no zoom on iOS). The note button says just "Notiz" there, so the header stays on one line. Co-Authored-By: Claude Opus 5.5 --- Doku/ui-patterns.md | 4 +++- frontend/src/features/UserAutocomplete.tsx | 6 +++--- frontend/src/features/triage/TaskDetailModal.tsx | 14 ++++++++------ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Doku/ui-patterns.md b/Doku/ui-patterns.md index f02c6e9..a2a3494 100644 --- a/Doku/ui-patterns.md +++ b/Doku/ui-patterns.md @@ -14,6 +14,7 @@ Eingesetzt beim Teilen (`UnifiedShareModal`), beim Hinzufügen zu Gruppen (`Grou - Bereits gewählte oder freigegebene Personen werden über `excludeUserIds` ausgeblendet. - **Einzelauswahl** (Teilen, Gruppen): `selectedUser` setzen; das Feld zeigt dann die gewählte Person mit ×. - **Mehrfachauswahl** (Aufgaben zuweisen): `selectedUser={null}` lassen und in `onSelectUser` zur Liste hinzufügen. Die Gewählten stehen als Chips (Avatar, Name, ×) über dem Feld, das Feld bleibt für die nächste Suche frei. +- Am Handy sind Feld, gewählte Person und Trefferzeilen 44 px hoch (Schrift 16 px, kein Zoom unter iOS), ab `sm` wie bisher 32 px. ## Profilbilder als Stapel @@ -64,7 +65,8 @@ Komponente: [CountBadge.tsx](../frontend/src/components/CountBadge.tsx), CSS-Kla - `muted` (Standard): reine Anzahl. Tabs der Aufgabenlisten und Listen, Organisationen, mobiles Menü. - Beide kommen aus den Theme-Variablen (`--accent-primary`/`--text-on-accent` bzw. `--text-muted`/`--bg-panel`) und passen sich damit jedem Theme an. - **Position (`position`):** - - `corner`: Benachrichtigungs-Badge oben rechts am nächsten `relative` Elternelement, mit Ring in der Seitenfarbe. In der Navigation und an den Tabs sitzt er **auf dem Text**: der Text steht in einem `relative inline-flex`-Span (mit `pr-3`, solange ein Zähler da ist), der Kreis bekommt `-top-0.5 -right-2`, damit er die obere Tab-Linie nicht anschneidet und nicht am Text klebt. In der mobilen Leiste sitzt er am Icon (`-top-1.5 -right-2.5`). + - `text`: an Beschriftungen (Navigation, Reiter der Aufgabenlisten und Listen, Workspace-Kopf). Der Zähler steht im Textfluss direkt hinter dem Wort, leicht angehoben, und schließt direkt an; nur sein Ring in Seitenfarbe hängt 2 px über den letzten Buchstaben, egal wie viele Stellen die Zahl hat; breitere Zahlen machen den Reiter breiter. Der Text steht dafür in einem `inline-flex items-center`-Span. + - `corner`: Benachrichtigungs-Badge oben rechts am nächsten `relative` Elternelement, mit Ring in der Seitenfarbe; für Icons, z. B. in der mobilen Leiste (`-top-1.5 -right-2.5`). - `inline`: im Textfluss. - Zahlen im mobilen Menü (`MobileActionContext`, `badge: number`) werden automatisch als `CountBadge` gezeigt; Text-Badges wie „2 aktiv“ bleiben Textmarken. - **Wichtig für neue Klassen:** Tailwind entfernt Klassen aus `@layer components`, die nicht wörtlich im Code stehen. Klassennamen deshalb nie zusammensetzen (`count-badge-${variant}`), sondern ausschreiben. diff --git a/frontend/src/features/UserAutocomplete.tsx b/frontend/src/features/UserAutocomplete.tsx index debcde6..def6d8c 100644 --- a/frontend/src/features/UserAutocomplete.tsx +++ b/frontend/src/features/UserAutocomplete.tsx @@ -103,7 +103,7 @@ export const UserAutocomplete: React.FC = ({ return (
{selectedUser ? ( -
+
@@ -148,7 +148,7 @@ export const UserAutocomplete: React.FC = ({ onFocus={() => setIsOpen(true)} onKeyDown={handleKeyDown} placeholder={placeholder} - className="w-full h-8 pl-8 pr-2.5 bg-input text-main border border-border rounded-btn focus:outline-none focus:border-accent text-xs placeholder:text-muted transition-colors disabled:opacity-50" + className="w-full h-11 sm:h-8 pl-8 pr-2.5 bg-input text-main border border-border rounded-btn focus:outline-none focus:border-accent text-base sm:text-xs placeholder:text-muted transition-colors disabled:opacity-50" /> {showResults && ( @@ -166,7 +166,7 @@ export const UserAutocomplete: React.FC = ({ type="button" onClick={() => handleSelect(u)} onMouseEnter={() => setHighlightedIndex(idx)} - className={`w-full px-2.5 py-1.5 flex items-center justify-between gap-2 text-left text-xs transition-colors cursor-pointer ${ + className={`w-full min-h-11 sm:min-h-0 px-2.5 py-1.5 flex items-center justify-between gap-2 text-left text-xs transition-colors cursor-pointer ${ isHighlighted ? 'bg-accent/15 text-main font-medium' : 'hover:bg-panel-elevated text-main' diff --git a/frontend/src/features/triage/TaskDetailModal.tsx b/frontend/src/features/triage/TaskDetailModal.tsx index 1cded09..70d40b2 100644 --- a/frontend/src/features/triage/TaskDetailModal.tsx +++ b/frontend/src/features/triage/TaskDetailModal.tsx @@ -287,7 +287,9 @@ const TaskDetailModalContent: React.FC className="btn-primary min-h-[38px] sm:min-h-0 h-9 sm:h-7 px-2.5 sm:px-2 text-xs flex items-center gap-1.5 font-semibold" > - In Notiz öffnen + {/* Short on phones, so the header buttons stay on one line */} + Notiz + In Notiz öffnen ) : !readOnly && (
@@ -3014,7 +3064,7 @@ export const TasksView: React.FC = ({
) : (
- {groupedTasks ? ( + {groupedTasks && groupedTasks.length > 0 ? ( /* GROUPED LIST */ groupedTasks.map((grp) => (
diff --git a/frontend/src/lib/viewSession.ts b/frontend/src/lib/viewSession.ts index 51f0f4e..85f0b8c 100644 --- a/frontend/src/lib/viewSession.ts +++ b/frontend/src/lib/viewSession.ts @@ -18,3 +18,27 @@ export function writeViewSession(view: string, state: T) { // storage blocked: the view simply starts fresh } } + +// Remembered per device as well (localStorage): a new tab or a restart of the installed app opens the +// same list again. This tab's own state wins, so two tabs can show different lists. +const DEVICE_PREFIX = 'mindsift_view_device:'; + +export function readViewMemory(view: string): Partial { + let device: Partial = {}; + try { + const raw = JSON.parse(localStorage.getItem(DEVICE_PREFIX + view) || '{}'); + if (raw && typeof raw === 'object') device = raw; + } catch { + // storage blocked or broken: the tab state alone + } + return { ...device, ...readViewSession(view) }; +} + +export function writeViewMemory(view: string, state: T) { + writeViewSession(view, state); + try { + localStorage.setItem(DEVICE_PREFIX + view, JSON.stringify(state)); + } catch { + // only a convenience + } +} From d11c156ed770d7dfb6fcc6fdbe6a211ec4578459 Mon Sep 17 00:00:00 2001 From: Sandro Date: Mon, 2 Nov 2026 13:13:14 +0100 Subject: [PATCH 4/5] style(ui): solid counters without the dark ring The accent counter was a 10-16 % tint, so on the phone nav bar the icon showed through; it now lies on an opaque panel. The ring in the page colour looked like a black shadow in dark themes and is gone; text counters overlap the word by 2 px instead. Co-Authored-By: Claude Opus 5.5 --- Doku/ui-patterns.md | 8 ++++---- frontend/src/index.css | 11 +++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Doku/ui-patterns.md b/Doku/ui-patterns.md index a2a3494..6aed6d4 100644 --- a/Doku/ui-patterns.md +++ b/Doku/ui-patterns.md @@ -59,14 +59,14 @@ Komponente: [FormSheet.tsx](../frontend/src/components/FormSheet.tsx), Tastatur- Komponente: [CountBadge.tsx](../frontend/src/components/CountBadge.tsx), CSS-Klassen `count-badge*` in [index.css](../frontend/src/index.css). Es gibt keine anderen Zähler-Stile mehr. -- **Form:** runder Kreis, mindestens 16 px, ab 100 als „99+“ (der Screenreader liest die echte Zahl). Bei 0 ausgeblendet, außer mit `showZero` (z. B. Mitgliederzahl, mobiles Menü). +- **Form:** runder, gefüllter Kreis mit dünnem Rand, ohne Schatten oder Ring, mindestens 16 px, ab 100 als „99+“ (der Screenreader liest die echte Zahl). Bei 0 ausgeblendet, außer mit `showZero` (z. B. Mitgliederzahl, mobiles Menü). - **Farbe (`variant`):** - `accent`: Hier wartet etwas auf dich. Navigationsleiste: Triage (Inbox), Aufgaben (offene Aufgaben), Listen (offene Einträge aller eigenen und geteilten Listen). Workspace hat keinen Zähler. - `muted` (Standard): reine Anzahl. Tabs der Aufgabenlisten und Listen, Organisationen, mobiles Menü. - - Beide kommen aus den Theme-Variablen (`--accent-primary`/`--text-on-accent` bzw. `--text-muted`/`--bg-panel`) und passen sich damit jedem Theme an. + - Beide kommen aus den Theme-Variablen (`--accent-primary`/`--text-on-accent` bzw. `--text-muted`/`--bg-panel`) und passen sich damit jedem Theme an. Der Akzent liegt auf einer deckenden Panel-Fläche, damit am Handy das Icon darunter nicht durchscheint. - **Position (`position`):** - - `text`: an Beschriftungen (Navigation, Reiter der Aufgabenlisten und Listen, Workspace-Kopf). Der Zähler steht im Textfluss direkt hinter dem Wort, leicht angehoben, und schließt direkt an; nur sein Ring in Seitenfarbe hängt 2 px über den letzten Buchstaben, egal wie viele Stellen die Zahl hat; breitere Zahlen machen den Reiter breiter. Der Text steht dafür in einem `inline-flex items-center`-Span. - - `corner`: Benachrichtigungs-Badge oben rechts am nächsten `relative` Elternelement, mit Ring in der Seitenfarbe; für Icons, z. B. in der mobilen Leiste (`-top-1.5 -right-2.5`). + - `text`: an Beschriftungen (Navigation, Reiter der Aufgabenlisten und Listen, Workspace-Kopf). Der Zähler steht im Textfluss direkt hinter dem Wort, leicht angehoben, und hängt immer 2 px über den letzten Buchstaben, egal wie viele Stellen die Zahl hat; breitere Zahlen machen den Reiter breiter. Der Text steht dafür in einem `inline-flex items-center`-Span. + - `corner`: Benachrichtigungs-Badge oben rechts am nächsten `relative` Elternelement; für Icons, z. B. in der mobilen Leiste (`-top-1.5 -right-2.5`). - `inline`: im Textfluss. - Zahlen im mobilen Menü (`MobileActionContext`, `badge: number`) werden automatisch als `CountBadge` gezeigt; Text-Badges wie „2 aktiv“ bleiben Textmarken. - **Wichtig für neue Klassen:** Tailwind entfernt Klassen aus `@layer components`, die nicht wörtlich im Code stehen. Klassennamen deshalb nie zusammensetzen (`count-badge-${variant}`), sondern ausschreiben. diff --git a/frontend/src/index.css b/frontend/src/index.css index 7d2f5d0..bd1671e 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -322,7 +322,8 @@ body { pointer-events: none; } .count-badge-accent { - background: var(--accent-dim); + /* The tint on an opaque panel: the badge stays solid on top of icons (phone nav bar) */ + background: linear-gradient(var(--accent-dim), var(--accent-dim)), var(--bg-panel); color: var(--accent-primary); border: 1px solid var(--accent-border); } @@ -336,16 +337,14 @@ body { top: -0.5rem; right: -0.5rem; z-index: 1; - /* ring in the page colour keeps the badge readable on top of borders */ - box-shadow: 0 0 0 2px var(--bg-app); } - /* In the text flow right after a label: only the ring hangs over the last letter (2 px), however many - digits; wider numbers make the tab wider instead of covering more of the word */ + /* In the text flow right after a label: hangs 2 px over the last letter, however many digits; wider + numbers make the tab wider instead of covering more of the word */ .count-badge-text { position: relative; top: -0.375rem; + margin-left: -2px; z-index: 1; - box-shadow: 0 0 0 2px var(--bg-app); } .badge-ok { From c2aff2ced6796060bb2945499e7b33a7f05694a0 Mon Sep 17 00:00:00 2001 From: Sandro Date: Mon, 2 Nov 2026 13:56:31 +0100 Subject: [PATCH 5/5] chore: release v0.58.0 Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 15 ++++++++++++++- README.md | 2 +- frontend/package-lock.json | 4 ++-- frontend/package.json | 2 +- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c83c02..aba0e39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V ## [Unreleased] +## [0.58.0] - 2026-11-02 + +### Added +- Aufgaben: „Alle Aufgaben“ ist standardmäßig nach Liste gruppiert. Aufgaben ohne Liste stehen als „Ohne Liste“ ganz oben, danach die Listen in der Reihenfolge der Reiter. Unter „Gruppe“ lässt sich wie bisher anders gruppieren; einzelne Listen bleiben ungruppiert. + +### Changed +- Gewählte Aufgabenliste, Listen-/Kanban-Ansicht und Gruppierung sowie die gewählte Einkaufsliste merkt sich MindSift jetzt pro Gerät, auch über einen Neustart der installierten App oder einen neuen Tab hinweg. +- Zähler an Reitern und in der Navigation stehen direkt hinter dem Wort und hängen immer nur 2 px über den letzten Buchstaben, auch bei zwei- und dreistelligen Zahlen. Sie sind überall gefüllt (am Handy schien vorher das Icon durch) und haben keinen dunklen Ring mehr. + +### Fixed +- Handy: In den Aufgaben-Details waren Aufgabenliste, Personen-Suche, Hashtag und Unteraufgabe deutlich kleiner als die übrigen Felder; jetzt sind alle gleich groß (auch beim Teilen und in Gruppen), und iOS zoomt beim Antippen nicht mehr hinein. Der Knopf zur verknüpften Notiz heißt dort kurz „Notiz“, damit der Kopf einzeilig bleibt. + ## [0.57.0] - 2026-11-02 ### Changed @@ -836,7 +848,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.57.0...dev +[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.58.0...dev +[0.58.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.57.0...v0.58.0 [0.57.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.56.0...v0.57.0 [0.56.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.55.0...v0.56.0 [0.55.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.54.0...v0.55.0 diff --git a/README.md b/README.md index 949fdcc..a367a2b 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.57.0-16a34a?style=flat-square)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/Version-v0.58.0-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 75e688a..3b2978f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "mindsift-frontend", - "version": "0.57.0", + "version": "0.58.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mindsift-frontend", - "version": "0.57.0", + "version": "0.58.0", "license": "AGPL-3.0-only", "dependencies": { "@fontsource-variable/inter": "5.3.0", diff --git a/frontend/package.json b/frontend/package.json index aecf55a..7588054 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "mindsift-frontend", "private": true, - "version": "0.57.0", + "version": "0.58.0", "license": "AGPL-3.0-only", "type": "module", "scripts": {