Merge dev into main (v0.60.0)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 29s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 29s
This commit is contained in:
commit
87411c42f7
8 files changed
+63
-85
No files matched your search
+11
-1
@@ -6,6 +6,15 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.60.0] - 2026-11-02
|
||||
|
||||
### Changed
|
||||
- Desktop: Das Fenster scrollt nicht mehr als Ganzes. Kopfleiste und Fußzeile stehen fest, nur der Bereich dazwischen scrollt; der Scrollbalken beginnt unter der Kopfleiste. Damit verschiebt sich beim Seitenwechsel nichts mehr, und der Rand neben der Kopfleiste braucht keine Sonderbehandlung.
|
||||
- Triage am Desktop: Kachel und Knöpfe bleiben stehen, die Warteschlange füllt den restlichen Platz und scrollt in sich.
|
||||
|
||||
### Fixed
|
||||
- Chrome: Die Kopfleiste scrollte seit 0.59.3 beim Scrollen mit aus dem Bild.
|
||||
|
||||
## [0.59.3] - 2026-11-02
|
||||
|
||||
### Fixed
|
||||
@@ -869,7 +878,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.59.3...dev
|
||||
[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.60.0...dev
|
||||
[0.60.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.59.3...v0.60.0
|
||||
[0.59.3]: https://git.dsit.me/sandrodach/MindSift/compare/v0.59.2...v0.59.3
|
||||
[0.59.2]: https://git.dsit.me/sandrodach/MindSift/compare/v0.59.1...v0.59.2
|
||||
[0.59.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.59.0...v0.59.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.59.3",
|
||||
"version": "0.60.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mindsift-frontend",
|
||||
"version": "0.59.3",
|
||||
"version": "0.60.0",
|
||||
"license": "AGPL-3.0-only",
|
||||
"dependencies": {
|
||||
"@fontsource-variable/inter": "5.3.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mindsift-frontend",
|
||||
"private": true,
|
||||
"version": "0.59.3",
|
||||
"version": "0.60.0",
|
||||
"license": "AGPL-3.0-only",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
+33
-20
@@ -206,21 +206,12 @@ export const App: React.FC = () => {
|
||||
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
||||
const connection = useConnectionState();
|
||||
|
||||
// Height of the app header as --app-header-h: the page background paints the header colour up to there,
|
||||
// so the scrollbar gutter on the right continues the header instead of showing the page colour
|
||||
const [headerEl, setHeaderEl] = useState<HTMLElement | null>(null);
|
||||
// Desktop: the window does not scroll; this area under the header does (phones keep the window scroll)
|
||||
const scrollAreaRef = useRef<HTMLDivElement>(null);
|
||||
// A new page starts at its top (the desktop scroll area would otherwise keep the old page's position)
|
||||
useEffect(() => {
|
||||
if (!headerEl || typeof ResizeObserver === 'undefined') return;
|
||||
const root = document.documentElement;
|
||||
const update = () => root.style.setProperty('--app-header-h', `${headerEl.offsetHeight}px`);
|
||||
update();
|
||||
const observer = new ResizeObserver(update);
|
||||
observer.observe(headerEl);
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
root.style.removeProperty('--app-header-h');
|
||||
};
|
||||
}, [headerEl]);
|
||||
scrollAreaRef.current?.scrollTo?.({ top: 0 });
|
||||
}, [activeTab]);
|
||||
const [showAbout, setShowAbout] = useState(false);
|
||||
const [showTriageTrash, setShowTriageTrash] = useState(false);
|
||||
const { update: availableUpdate, dismiss: dismissUpdate } = useUpdateCheck();
|
||||
@@ -848,7 +839,7 @@ export const App: React.FC = () => {
|
||||
if (isPhoneWidth()) {
|
||||
openMobileTriageCapture();
|
||||
} else {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
scrollAreaRef.current?.scrollTo?.({ top: 0, behavior: 'smooth' });
|
||||
setCaptureFocusCount((c) => c + 1);
|
||||
}
|
||||
};
|
||||
@@ -920,13 +911,12 @@ export const App: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`bg-canvas text-main flex flex-col font-sans selection:bg-accent/30 ${
|
||||
className={`bg-canvas text-main flex flex-col font-sans selection:bg-accent/30 sm:h-[100dvh] sm:overflow-hidden ${
|
||||
activeTab === 'workspace' ? 'h-[100dvh] overflow-hidden' : 'min-h-[100dvh]'
|
||||
}`}
|
||||
>
|
||||
{/* Top Header Bar: Floating on mobile, solid panel on desktop */}
|
||||
<header
|
||||
ref={setHeaderEl}
|
||||
className="sticky top-0 z-30 bg-canvas/85 backdrop-blur-md sm:bg-panel sm:border-b sm:border-border sm:shadow-sm"
|
||||
>
|
||||
{/* Mobile Header: Floating Bar with Logo Action Dropdown Left & Profile Avatar Right */}
|
||||
@@ -1306,14 +1296,36 @@ export const App: React.FC = () => {
|
||||
)}
|
||||
|
||||
{/* Main Container */}
|
||||
{/* The workspace fills the window exactly and scrolls only inside the note, never the page */}
|
||||
{/* Desktop: the window never scrolls, only this area under the header (room for the scrollbar kept on
|
||||
both sides, so the content stays centred under the header and nothing shifts between pages). The
|
||||
workspace fills it exactly and scrolls only inside the note. Phones scroll the window as before. */}
|
||||
<div
|
||||
ref={scrollAreaRef}
|
||||
data-app-scroll
|
||||
className={`flex-1 flex flex-col min-h-0 sm:[scrollbar-gutter:stable_both-edges] ${
|
||||
activeTab === 'workspace' ? 'sm:overflow-hidden' : 'sm:overflow-y-auto'
|
||||
}`}
|
||||
>
|
||||
<main
|
||||
className={`flex-1 max-w-6xl w-full mx-auto px-3 sm:px-4 pt-3 pb-24 ${
|
||||
activeTab === 'workspace' ? 'min-h-0 flex flex-col sm:pb-0' : 'sm:pb-6'
|
||||
activeTab === 'workspace'
|
||||
? 'min-h-0 flex flex-col sm:pb-0'
|
||||
: activeTab === 'triage'
|
||||
? 'sm:min-h-0 sm:flex sm:flex-col sm:pb-6'
|
||||
: 'sm:pb-6'
|
||||
}`}
|
||||
>
|
||||
<ViewErrorBoundary onReset={() => setActiveTab('triage')}>
|
||||
<section key={dataEpoch} className={activeTab === 'workspace' ? 'flex-1 min-h-0 flex flex-col' : undefined}>
|
||||
<section
|
||||
key={dataEpoch}
|
||||
className={
|
||||
activeTab === 'workspace'
|
||||
? 'flex-1 min-h-0 flex flex-col'
|
||||
: activeTab === 'triage'
|
||||
? 'sm:flex-1 sm:min-h-0 sm:flex sm:flex-col' // the queue fills the rest of the height (TriageDeck)
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{activeTab === 'triage' && (
|
||||
<TriageDeck
|
||||
deck={deck}
|
||||
@@ -1393,6 +1405,7 @@ export const App: React.FC = () => {
|
||||
</section>
|
||||
</ViewErrorBoundary>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<AppFooter
|
||||
legal={authStatus.legal}
|
||||
|
||||
@@ -554,11 +554,15 @@ export const TasksView: React.FC<TasksViewProps> = ({
|
||||
setKanbanColHeight(null);
|
||||
return;
|
||||
}
|
||||
// From the board's top to the window's bottom; then take back what still overflows (footer, paddings)
|
||||
const top = el.getBoundingClientRect().top + window.scrollY;
|
||||
setKanbanColHeight(Math.max(320, window.innerHeight - top - 16));
|
||||
// From the board's top to the bottom of the app's scroll area (the desktop window itself does not
|
||||
// scroll); then take back what still overflows (paddings)
|
||||
const area = el.closest<HTMLElement>('[data-app-scroll]');
|
||||
const areaTop = area ? area.getBoundingClientRect().top - area.scrollTop : -window.scrollY;
|
||||
const areaHeight = area ? area.clientHeight : window.innerHeight;
|
||||
const top = el.getBoundingClientRect().top - areaTop;
|
||||
setKanbanColHeight(Math.max(320, areaHeight - top - 16));
|
||||
requestAnimationFrame(() => {
|
||||
const over = document.documentElement.scrollHeight - window.innerHeight;
|
||||
const over = area ? area.scrollHeight - area.clientHeight : document.documentElement.scrollHeight - window.innerHeight;
|
||||
if (over > 0) setKanbanColHeight((h) => (h ? Math.max(320, h - over) : h));
|
||||
});
|
||||
};
|
||||
|
||||
@@ -488,7 +488,9 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="max-w-2xl mx-auto select-none space-y-4">
|
||||
// Desktop: a column filling the height under the header; card and buttons keep their size, the queue
|
||||
// takes the rest and scrolls inside (the page only scrolls when the window is too low even for that)
|
||||
<div className="w-full max-w-2xl mx-auto select-none space-y-4 sm:flex-1 sm:min-h-0 sm:flex sm:flex-col">
|
||||
{/* Top Deck Status & Undo Bar */}
|
||||
<div className="flex items-center justify-between px-1">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -931,7 +933,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
|
||||
|
||||
{/* Waiting Quick Dump Queue List (Touch-sized & Swipeable Left/Right) */}
|
||||
{cards.length > 1 && (
|
||||
<div className="pt-2" ref={queueRef}>
|
||||
<div className="pt-2 sm:flex-1 sm:min-h-[9rem] sm:flex sm:flex-col" ref={queueRef}>
|
||||
{/* Phones: the queue stays folded so the card and its buttons fit on the screen */}
|
||||
<button
|
||||
type="button"
|
||||
@@ -955,11 +957,11 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
|
||||
</button>
|
||||
{/* Folds open/closed on phones; always open from sm upwards */}
|
||||
<div
|
||||
className={`grid transition-[grid-template-rows,opacity,visibility] duration-300 ease-out sm:grid-rows-[1fr] sm:opacity-100 sm:visible ${
|
||||
className={`grid transition-[grid-template-rows,opacity,visibility] duration-300 ease-out sm:flex-1 sm:min-h-0 sm:grid-rows-[1fr] sm:opacity-100 sm:visible ${
|
||||
showQueueMobile ? 'grid-rows-[1fr] opacity-100 visible' : 'grid-rows-[0fr] opacity-0 invisible'
|
||||
}`}
|
||||
>
|
||||
<div className="overflow-hidden min-h-0">
|
||||
<div className="overflow-hidden min-h-0 sm:flex sm:flex-col">
|
||||
<div className="flex items-center justify-between mb-2 px-1 pt-2 sm:pt-0">
|
||||
<span className="text-xs font-semibold uppercase tracking-wider text-muted flex items-center gap-1.5">
|
||||
<Layers className="w-3.5 h-3.5 text-accent" />
|
||||
@@ -970,7 +972,7 @@ export const TriageDeck: React.FC<TriageDeckProps> = ({
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="panel-surface divide-y divide-border max-h-80 overflow-y-auto overflow-x-hidden">
|
||||
<div className="panel-surface divide-y divide-border max-h-80 sm:max-h-none sm:flex-1 sm:min-h-0 overflow-y-auto overflow-x-hidden">
|
||||
{cards.slice(1).map((item) => (
|
||||
<SwipeableQueueItem
|
||||
key={item.id}
|
||||
|
||||
@@ -29,45 +29,6 @@
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
/* Room for the scrollbar is always kept: pages that scroll (e.g. a wide Kanban board) and pages that do
|
||||
not keep the same width, so nothing shifts when switching tabs. Overlay scrollbars (phones) are not
|
||||
affected. */
|
||||
html {
|
||||
scrollbar-gutter: stable;
|
||||
}
|
||||
/* Chrome paints an empty gutter (page without scrollbar) plain white, ignoring the page background. There
|
||||
the scrollbar is always shown instead: without anything to scroll it is an empty track, coloured like
|
||||
the header and page by body::-webkit-scrollbar below. Firefox keeps the gutter, which it paints with the
|
||||
html background. */
|
||||
@supports selector(::-webkit-scrollbar) {
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
}
|
||||
/* Header colour down to the header's height (--app-header-h, set by App), then the page colour. The header's
|
||||
bottom line is its own layer on top, since the theme gives it as a translucent colour. Defined on body:
|
||||
Chrome resolves variables for the page scrollbar from there (html sees it through inheritance too). */
|
||||
:root,
|
||||
body {
|
||||
--header-gutter-bg: linear-gradient(
|
||||
to bottom,
|
||||
transparent calc(var(--app-header-h, 0px) - 1px),
|
||||
var(--border-color) calc(var(--app-header-h, 0px) - 1px),
|
||||
var(--border-color) var(--app-header-h, 0px),
|
||||
transparent var(--app-header-h, 0px)
|
||||
),
|
||||
linear-gradient(to bottom, var(--bg-panel) var(--app-header-h, 0px), var(--bg-app) var(--app-header-h, 0px));
|
||||
}
|
||||
/* The gutter shows the page background; on the desktop it carries the header colour (and its bottom
|
||||
line) down to the header's height (--app-header-h, set by App), fixed like the sticky header, so the
|
||||
header seems to run to the window edge */
|
||||
@media (min-width: 768px) {
|
||||
html {
|
||||
background: var(--header-gutter-bg);
|
||||
background-attachment: fixed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--bg-app);
|
||||
color: var(--text-main);
|
||||
@@ -86,18 +47,6 @@ body {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* Chrome takes the page scrollbar's style from body (html:: does not apply) and paints white behind a
|
||||
transparent track, where Firefox shows the html background: the page scrollbar gets the same
|
||||
background as html above, on the desktop with the header colour on top */
|
||||
body::-webkit-scrollbar {
|
||||
background: var(--bg-app);
|
||||
}
|
||||
@media (min-width: 768px) {
|
||||
body::-webkit-scrollbar {
|
||||
background: var(--header-gutter-bg);
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: var(--scrollbar-thumb);
|
||||
border-radius: var(--radius-btn);
|
||||
|
||||
Reference in new issue
Block a user