diff --git a/CHANGELOG.md b/CHANGELOG.md index 60e78bc..b34faaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V ## [Unreleased] +## [0.63.1] - 2026-11-02 + +### Fixed +- Notizen am Desktop: Die Knopfleiste an einem Punkt (Fokus, Auslagern, Löschen …) erscheint nur noch, während sich die Maus über dem Punkt bewegt, und verschwindet etwa eine Sekunde später. Eine ruhende Maus, auch beim Tippen, lässt sie nicht mehr über dem Text stehen. Auf der Leiste selbst bleibt sie sichtbar. + ## [0.63.0] - 2026-11-02 ### Added @@ -902,7 +907,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.63.0...dev +[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.63.1...dev +[0.63.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.63.0...v0.63.1 [0.63.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.62.0...v0.63.0 [0.62.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.61.0...v0.62.0 [0.61.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.60.0...v0.61.0 diff --git a/README.md b/README.md index f549a3e..d3217ef 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.63.0-16a34a?style=flat-square)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/Version-v0.63.1-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 2cafe65..9b92804 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "mindsift-frontend", - "version": "0.63.0", + "version": "0.63.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mindsift-frontend", - "version": "0.63.0", + "version": "0.63.1", "license": "AGPL-3.0-only", "dependencies": { "@fontsource-variable/inter": "5.3.0", diff --git a/frontend/package.json b/frontend/package.json index 1e9367d..b45a875 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "mindsift-frontend", "private": true, - "version": "0.63.0", + "version": "0.63.1", "license": "AGPL-3.0-only", "type": "module", "scripts": { diff --git a/frontend/src/features/workspace/OutlinerBlock.tsx b/frontend/src/features/workspace/OutlinerBlock.tsx index d013c27..a85bb19 100644 --- a/frontend/src/features/workspace/OutlinerBlock.tsx +++ b/frontend/src/features/workspace/OutlinerBlock.tsx @@ -67,6 +67,9 @@ function slashRank(c: SlashCommand, q: string): number { return 4; } +// How long the action bubble stays after the last mouse move over a bullet +const BUBBLE_IDLE_MS = 1000; + const SLASH_COMMANDS: SlashCommand[] = [ { type: 'todo', label: '/todo', desc: 'Aufgabe mit Checkbox', icon: }, { type: 'numbered', label: '/number', desc: 'Nummerierte Liste (1., a., i.)', icon: , aliases: ['nummer', 'liste', 'ol'] }, @@ -209,6 +212,28 @@ export const OutlinerBlock: React.FC = ({ const [showLinkModal, setShowLinkModal] = useState(false); const [showTodoMenu, setShowTodoMenu] = useState(false); + // Action bubble with a real pointer: shown only while the mouse moves over the row (until a moment after + // the last move), while it rests on the bubble or a menu of it is open; a resting mouse must not leave it + // over the text. Touch devices keep showing it at the focused bullet. + const hasHover = useMemo(() => typeof window !== 'undefined' && !!window.matchMedia?.('(hover: hover)').matches, []); + const [pointerMoving, setPointerMoving] = useState(false); + const [pointerOnBubble, setPointerOnBubble] = useState(false); + const pointerIdleTimerRef = useRef(null); + const handleRowMouseMove = () => { + if (!hasHover) return; + setPointerMoving(true); + if (pointerIdleTimerRef.current !== null) window.clearTimeout(pointerIdleTimerRef.current); + pointerIdleTimerRef.current = window.setTimeout(() => setPointerMoving(false), BUBBLE_IDLE_MS); + }; + const handleRowMouseLeave = () => { + if (pointerIdleTimerRef.current !== null) window.clearTimeout(pointerIdleTimerRef.current); + pointerIdleTimerRef.current = null; + setPointerMoving(false); + }; + useEffect(() => () => { + if (pointerIdleTimerRef.current !== null) window.clearTimeout(pointerIdleTimerRef.current); + }, []); + const textareaRef = useRef(null); const debounceTimerRef = useRef(null); const latestBlockRef = useRef(block); @@ -1087,9 +1112,13 @@ export const OutlinerBlock: React.FC = ({ const isEmpty = (!localContent || localContent.trim() === '') && !hasChildren; + const showBubble = hasHover ? pointerMoving || pointerOnBubble || showTodoMenu : activeFocus; + return (
= ({ {/* Floating Actions Overlay Bubble (Zoom, Auslagern, Löschen, TODO-Menü, Text-Umwandlung) */}
setPointerOnBubble(true)} + onMouseLeave={() => setPointerOnBubble(false)} className={`absolute right-1 sm:right-2 top-0.5 sm:top-1 z-20 bg-panel/90 backdrop-blur-sm border border-border shadow-xs rounded-pill px-1 sm:px-1.5 py-0.5 flex items-center gap-0.5 sm:gap-1 transition-opacity ${ - activeFocus + showBubble ? 'opacity-100 pointer-events-auto' - : // Hover only with a real pointer: a tap on a phone would make the invisible buttons clickable - 'opacity-0 [@media(hover:hover)]:group-hover:opacity-100 focus-within:opacity-100 pointer-events-none [@media(hover:hover)]:group-hover:pointer-events-auto focus-within:pointer-events-auto' + : // Hidden buttons must not take clicks; keyboard focus inside still shows it + 'opacity-0 pointer-events-none focus-within:opacity-100 focus-within:pointer-events-auto' }`} > {/* Convert back to bullet for headings, callouts, dividers, code and numbered items */}