fix(ui): even footer line, mode switch fits the user menu and shows every choice
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m23s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m23s
chore(release): v0.14.1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3f0e596356
commit
61541b2851
5 files changed
+32
-36
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]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.14.1] - 2026-10-30
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Benutzermenü: Der Hell/Dunkel-Schalter ragte aus dem Menü heraus und zeigte „Wie System“ nicht an, wenn sich dabei das Theme nicht änderte. Er steht jetzt in einer eigenen Zeile (Hell · Dunkel · System) und zeigt jede Auswahl sofort.
|
||||||
|
- Footer: eine gleichmäßige, kleinere Zeile („MindSift v… · Über MindSift“, rechts die Rechtstexte) in einheitlicher Schrift statt fett/Monospace gemischt, die schief wirkte.
|
||||||
|
|
||||||
## [0.14.0] - 2026-10-30
|
## [0.14.0] - 2026-10-30
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
@@ -334,7 +340,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.14.0...dev
|
[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.14.1...dev
|
||||||
|
[0.14.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.14.0...v0.14.1
|
||||||
[0.14.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.13.0...v0.14.0
|
[0.14.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.13.0...v0.14.0
|
||||||
[0.13.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.12.2...v0.13.0
|
[0.13.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.12.2...v0.13.0
|
||||||
[0.12.2]: https://git.dsit.me/sandrodach/MindSift/compare/v0.12.1...v0.12.2
|
[0.12.2]: https://git.dsit.me/sandrodach/MindSift/compare/v0.12.1...v0.12.2
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "mindsift-frontend",
|
"name": "mindsift-frontend",
|
||||||
"version": "0.14.0",
|
"version": "0.14.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mindsift-frontend",
|
"name": "mindsift-frontend",
|
||||||
"version": "0.14.0",
|
"version": "0.14.1",
|
||||||
"license": "Unlicense",
|
"license": "Unlicense",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fontsource-variable/inter": "5.3.0",
|
"@fontsource-variable/inter": "5.3.0",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "mindsift-frontend",
|
"name": "mindsift-frontend",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.14.0",
|
"version": "0.14.1",
|
||||||
"license": "Unlicense",
|
"license": "Unlicense",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -9,37 +9,21 @@ interface AppFooterProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const AppFooter: React.FC<AppFooterProps> = ({ legal, onOpenAbout }) => {
|
export const AppFooter: React.FC<AppFooterProps> = ({ legal, onOpenAbout }) => {
|
||||||
|
// One small, even line: same font, size and weight for every part (mixed mono/bold looked crooked)
|
||||||
|
const item = 'leading-none hover:text-muted transition-colors cursor-pointer';
|
||||||
return (
|
return (
|
||||||
<footer className="hidden sm:flex shrink-0 w-full max-w-6xl mx-auto px-4 py-2 items-center justify-between text-[11px] text-faint select-none">
|
<footer className="hidden sm:flex shrink-0 w-full max-w-6xl mx-auto px-4 py-1.5 items-center justify-between text-[10px] leading-none text-faint select-none">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-1.5">
|
||||||
<button
|
<button type="button" onClick={onOpenAbout} className={item} title="Über MindSift & Versionsdetails">
|
||||||
type="button"
|
MindSift {APP_VERSION}
|
||||||
onClick={onOpenAbout}
|
|
||||||
className="font-semibold text-muted hover:text-main transition-colors cursor-pointer"
|
|
||||||
>
|
|
||||||
MindSift
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<span aria-hidden>·</span>
|
||||||
type="button"
|
<button type="button" onClick={onOpenAbout} className={item}>
|
||||||
onClick={onOpenAbout}
|
|
||||||
className="font-mono hover:text-main transition-colors cursor-pointer"
|
|
||||||
title="Über MindSift & Versionsdetails"
|
|
||||||
>
|
|
||||||
{APP_VERSION}
|
|
||||||
</button>
|
|
||||||
<span className="text-border">·</span>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={onOpenAbout}
|
|
||||||
className="hover:text-main transition-colors cursor-pointer underline-offset-2 hover:underline"
|
|
||||||
>
|
|
||||||
Über MindSift
|
Über MindSift
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-3">
|
<LegalLinks legal={legal} className="!text-[10px] !text-faint !gap-x-3 leading-none" />
|
||||||
<LegalLinks legal={legal} className="!text-faint gap-3" />
|
|
||||||
</div>
|
|
||||||
</footer>
|
</footer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Monitor, Moon, Sun } from 'lucide-react';
|
import { Monitor, Moon, Sun } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
AVAILABLE_THEMES,
|
AVAILABLE_THEMES,
|
||||||
@@ -25,13 +25,14 @@ export const ThemeQuickPicker: React.FC<ThemeQuickPickerProps> = ({ activeTheme,
|
|||||||
const mode = active?.mode ?? 'dark';
|
const mode = active?.mode ?? 'dark';
|
||||||
const customThemes = themes.filter((t) => t.isCustom);
|
const customThemes = themes.filter((t) => t.isCustom);
|
||||||
const dot = touch ? 'w-9 h-9' : 'w-7 h-7';
|
const dot = touch ? 'w-9 h-9' : 'w-7 h-7';
|
||||||
const followSystem = getFollowSystem();
|
// Kept in state: choosing "System" may not change the theme, but the selection must still show
|
||||||
|
const [followSystem, setFollow] = useState(getFollowSystem);
|
||||||
const pick = (id: ThemeId) => onChange(resolveTheme(id));
|
const pick = (id: ThemeId) => onChange(resolveTheme(id));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex items-center justify-between gap-1">
|
||||||
<div className="flex items-center gap-1.5" role="radiogroup" aria-label="Farbfamilie">
|
<div className="flex w-full items-center justify-between" role="radiogroup" aria-label="Farbfamilie">
|
||||||
{THEME_FAMILIES.map((f) => {
|
{THEME_FAMILIES.map((f) => {
|
||||||
const preview = AVAILABLE_THEMES.find((t) => t.id === (mode === 'light' ? f.light : f.dark));
|
const preview = AVAILABLE_THEMES.find((t) => t.id === (mode === 'light' ? f.light : f.dark));
|
||||||
const selected = active?.family === f.id;
|
const selected = active?.family === f.id;
|
||||||
@@ -53,7 +54,9 @@ export const ThemeQuickPicker: React.FC<ThemeQuickPickerProps> = ({ activeTheme,
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center rounded-full bg-panel-elevated p-0.5" role="radiogroup" aria-label="Hell oder dunkel">
|
</div>
|
||||||
|
{/* Own row: three labelled segments, so it fits narrow menus */}
|
||||||
|
<div className="grid grid-cols-3 rounded-full bg-panel-elevated p-0.5" role="radiogroup" aria-label="Hell oder dunkel">
|
||||||
{(['light', 'dark', 'auto'] as const).map((m) => {
|
{(['light', 'dark', 'auto'] as const).map((m) => {
|
||||||
const family = active?.family ? THEME_FAMILIES.find((f) => f.id === active.family) : undefined;
|
const family = active?.family ? THEME_FAMILIES.find((f) => f.id === active.family) : undefined;
|
||||||
const selected = m === 'auto' ? followSystem : !followSystem && !!active && mode === m;
|
const selected = m === 'auto' ? followSystem : !followSystem && !!active && mode === m;
|
||||||
@@ -68,11 +71,12 @@ export const ThemeQuickPicker: React.FC<ThemeQuickPickerProps> = ({ activeTheme,
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!family) return;
|
if (!family) return;
|
||||||
setFollowSystem(m === 'auto');
|
setFollowSystem(m === 'auto');
|
||||||
|
setFollow(m === 'auto');
|
||||||
onChange(m === 'auto' ? resolveTheme(family.dark) : family[m]);
|
onChange(m === 'auto' ? resolveTheme(family.dark) : family[m]);
|
||||||
}}
|
}}
|
||||||
title={m === 'light' ? 'Hell' : m === 'dark' ? 'Dunkel' : 'Wie System'}
|
title={m === 'light' ? 'Hell' : m === 'dark' ? 'Dunkel' : 'Wie System'}
|
||||||
aria-label={m === 'light' ? 'Hell' : m === 'dark' ? 'Dunkel' : 'Wie System'}
|
aria-label={m === 'light' ? 'Hell' : m === 'dark' ? 'Dunkel' : 'Wie System'}
|
||||||
className={`${touch ? 'h-9 w-9' : 'h-7 w-7'} rounded-full flex items-center justify-center transition-colors cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed ${
|
className={`${touch ? 'h-9' : 'h-7'} rounded-full flex items-center justify-center gap-1 text-[11px] font-medium transition-colors cursor-pointer disabled:opacity-40 disabled:cursor-not-allowed ${
|
||||||
selected ? 'bg-panel text-accent shadow-sm' : 'text-muted hover:text-main'
|
selected ? 'bg-panel text-accent shadow-sm' : 'text-muted hover:text-main'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@@ -83,10 +87,10 @@ export const ThemeQuickPicker: React.FC<ThemeQuickPickerProps> = ({ activeTheme,
|
|||||||
) : (
|
) : (
|
||||||
<Monitor className="w-3.5 h-3.5" />
|
<Monitor className="w-3.5 h-3.5" />
|
||||||
)}
|
)}
|
||||||
|
<span>{m === 'light' ? 'Hell' : m === 'dark' ? 'Dunkel' : 'System'}</span>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{customThemes.length > 0 && (
|
{customThemes.length > 0 && (
|
||||||
<div className="flex flex-wrap gap-1">
|
<div className="flex flex-wrap gap-1">
|
||||||
@@ -96,6 +100,7 @@ export const ThemeQuickPicker: React.FC<ThemeQuickPickerProps> = ({ activeTheme,
|
|||||||
type="button"
|
type="button"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setFollowSystem(false);
|
setFollowSystem(false);
|
||||||
|
setFollow(false);
|
||||||
onChange(t.id);
|
onChange(t.id);
|
||||||
}}
|
}}
|
||||||
className={`h-7 px-2 rounded-full text-[11px] flex items-center gap-1.5 border transition-colors cursor-pointer ${
|
className={`h-7 px-2 rounded-full text-[11px] flex items-center gap-1.5 border transition-colors cursor-pointer ${
|
||||||
|
|||||||
Reference in new issue
Block a user