Merge dev into main (v0.27.1)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 28s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 28s
This commit is contained in:
commit
e5dd797201
7 files changed
+71
-13
No files matched your search
+7
-1
@@ -6,6 +6,11 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.27.1] - 2026-10-31
|
||||
|
||||
### Fixed
|
||||
- Notizen am Handy: Die Leiste zum Ein- und Ausrücken über der Tastatur blieb stehen, wenn man die Tastatur schloss (z. B. mit der Zurück-Taste), und verdeckte die untere Navigation. Sie erscheint jetzt nur, solange die Tastatur offen ist; ein Tipp in den Punkt holt beides zurück.
|
||||
|
||||
## [0.27.0] - 2026-10-31
|
||||
|
||||
### Changed
|
||||
@@ -539,7 +544,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.27.0...dev
|
||||
[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.27.1...dev
|
||||
[0.27.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.27.0...v0.27.1
|
||||
[0.27.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.26.1...v0.27.0
|
||||
[0.26.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.26.0...v0.26.1
|
||||
[0.26.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.25.1...v0.26.0
|
||||
|
||||
@@ -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.27.0",
|
||||
"version": "0.27.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mindsift-frontend",
|
||||
"version": "0.27.0",
|
||||
"version": "0.27.1",
|
||||
"license": "Unlicense",
|
||||
"dependencies": {
|
||||
"@fontsource-variable/inter": "5.3.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mindsift-frontend",
|
||||
"private": true,
|
||||
"version": "0.27.0",
|
||||
"version": "0.27.1",
|
||||
"license": "Unlicense",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ArrowDown, ArrowUp, IndentDecrease, IndentIncrease } from 'lucide-react
|
||||
import { useKeyboardInset } from '../../lib/useKeyboardInset';
|
||||
|
||||
// Phones and tablets have no Tab key: while a bullet is being edited, a slim bar sits right above the
|
||||
// on-screen keyboard with outdent / indent and move up / down (the bullet takes its children along).
|
||||
// on-screen keyboard (only while it is open) with outdent / indent and move up / down (the bullet takes its children along).
|
||||
export const isTouchOnly =
|
||||
typeof window !== 'undefined' &&
|
||||
!!window.matchMedia?.('(hover: none) and (pointer: coarse)').matches &&
|
||||
@@ -19,7 +19,7 @@ interface MobileOutlineBarProps {
|
||||
|
||||
export const MobileOutlineBar: React.FC<MobileOutlineBarProps> = ({ onOutdent, onIndent, onMoveUp, onMoveDown }) => {
|
||||
// iOS keeps fixed elements behind the keyboard: lift the bar by the keyboard's height
|
||||
const { inset } = useKeyboardInset();
|
||||
const { inset, keyboardOpen } = useKeyboardInset();
|
||||
|
||||
const button = (label: string, icon: React.ReactNode, action: () => void) => (
|
||||
<button
|
||||
@@ -36,6 +36,9 @@ export const MobileOutlineBar: React.FC<MobileOutlineBarProps> = ({ onOutdent, o
|
||||
</button>
|
||||
);
|
||||
|
||||
// Keyboard closed (e.g. back button) while the bullet keeps its focus: no bar over the bottom navigation
|
||||
if (!keyboardOpen) return null;
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
className="fixed left-0 right-0 z-[70] flex items-center justify-center gap-2 px-3 py-1.5 bg-panel/95 backdrop-blur-md border-t border-border"
|
||||
|
||||
@@ -26,10 +26,30 @@ describe('useKeyboardInset', () => {
|
||||
const vv = mockVisualViewport(844);
|
||||
const { result } = renderHook(() => useKeyboardInset());
|
||||
expect(result.current.inset).toBe(0);
|
||||
expect(result.current.keyboardOpen).toBe(false);
|
||||
act(() => vv.open(336));
|
||||
expect(result.current.inset).toBe(336);
|
||||
expect(result.current.visibleHeight).toBe(508);
|
||||
expect(result.current.keyboardOpen).toBe(true);
|
||||
act(() => vv.open(0));
|
||||
expect(result.current.inset).toBe(0);
|
||||
expect(result.current.keyboardOpen).toBe(false);
|
||||
});
|
||||
|
||||
it('notices the keyboard also when the page shrinks instead (no inset)', () => {
|
||||
const vv = mockVisualViewport(844);
|
||||
const { result } = renderHook(() => useKeyboardInset());
|
||||
act(() => {
|
||||
Object.defineProperty(window, 'innerHeight', { value: 508, configurable: true });
|
||||
vv.open(336);
|
||||
});
|
||||
expect(result.current.inset).toBe(0);
|
||||
expect(result.current.keyboardOpen).toBe(true);
|
||||
// Browser bars appearing are not a keyboard
|
||||
act(() => {
|
||||
Object.defineProperty(window, 'innerHeight', { value: 788, configurable: true });
|
||||
vv.open(56);
|
||||
});
|
||||
expect(result.current.keyboardOpen).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -1,13 +1,42 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
// Space the on-screen keyboard takes at the bottom (0 without keyboard) and the height that stays
|
||||
// visible. Fixed bottom sheets use it to sit right above the keyboard instead of behind it (iOS keeps
|
||||
// fixed elements on the layout viewport; on Android the inset is usually 0 because the page shrinks).
|
||||
export function useKeyboardInset(): { inset: number; visibleHeight: number } {
|
||||
// Tallest visible height seen at the current width, i.e. the height without keyboard. Tracked from app
|
||||
// start, so it is known before the first field is focused; a new width (rotation) starts over.
|
||||
let fullHeight = 0;
|
||||
let fullWidth = 0;
|
||||
function noteFullHeight() {
|
||||
const vv = window.visualViewport;
|
||||
if (!vv) return;
|
||||
if (window.innerWidth !== fullWidth) {
|
||||
fullWidth = window.innerWidth;
|
||||
fullHeight = 0;
|
||||
}
|
||||
fullHeight = Math.max(fullHeight, Math.round(vv.height));
|
||||
}
|
||||
if (typeof window !== 'undefined' && window.visualViewport) {
|
||||
noteFullHeight();
|
||||
window.visualViewport.addEventListener('resize', noteFullHeight);
|
||||
}
|
||||
|
||||
// More than browser bars appearing or disappearing (about 60 px)
|
||||
const KEYBOARD_MIN_PX = 150;
|
||||
|
||||
// Space the on-screen keyboard takes at the bottom (0 without keyboard), the height that stays visible
|
||||
// and whether a keyboard is open. Fixed bottom sheets use the inset to sit right above the keyboard
|
||||
// instead of behind it (iOS keeps fixed elements on the layout viewport; on Android the inset is usually
|
||||
// 0 because the page shrinks). keyboardOpen compares with the height without keyboard, so it works in
|
||||
// both cases; without visualViewport it is unknown and reported as open.
|
||||
export function useKeyboardInset(): { inset: number; visibleHeight: number; keyboardOpen: boolean } {
|
||||
const read = () => {
|
||||
const vv = typeof window !== 'undefined' ? window.visualViewport : null;
|
||||
if (!vv) return { inset: 0, visibleHeight: typeof window !== 'undefined' ? window.innerHeight : 0 };
|
||||
return { inset: Math.max(0, Math.round(window.innerHeight - vv.height - vv.offsetTop)), visibleHeight: Math.round(vv.height) };
|
||||
if (!vv) return { inset: 0, visibleHeight: typeof window !== 'undefined' ? window.innerHeight : 0, keyboardOpen: true };
|
||||
noteFullHeight();
|
||||
const visibleHeight = Math.round(vv.height);
|
||||
return {
|
||||
inset: Math.max(0, Math.round(window.innerHeight - vv.height - vv.offsetTop)),
|
||||
visibleHeight,
|
||||
keyboardOpen: visibleHeight < fullHeight - KEYBOARD_MIN_PX,
|
||||
};
|
||||
};
|
||||
const [state, setState] = useState(read);
|
||||
|
||||
|
||||
Reference in new issue
Block a user