diff --git a/AGENTS.md b/AGENTS.md index b007ef6..50e43c3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,6 +38,12 @@ Die Linter-Warnungen `react-hooks/exhaustive-deps` gibt es schon länger; neuer ``` - **Zeitabhängiger Code** (Alarme, Tageszusammenfassung, Begrüßungen, Termine): nie `time.Now()` dort, wo eine übergebene Zeit gemeint ist. Die betroffenen Go-Tests zusätzlich mit verschiedenen `TZ`-Werten laufen lassen (z. B. `Europe/Berlin`, `America/Los_Angeles`, `Asia/Tokyo`). Ein uhrzeitabhängiger Test hat schon einmal einen Release verhindert, weil er nur vormittags grün war. +## Screenshots + +- Bilder in `screenshots/` (README) sind **WebP**, keine PNG: Desktop und Theme-Raster 1280 px breit, Handy 480 px, Qualität 90. Das bringt rund ein Fünftel der PNG-Größe bei sauberer Schrift. +- Erzeugt werden sie mit `e2e/screenshots.sh`; das Skript nimmt PNGs auf und wandelt sie am Ende mit `cwebp` (Alpine-Container) um. Einzelne Bilder neu aufnehmen: denselben Ablauf aus `screenshots.mjs` verwenden und mit dem Umwandlungsschritt aus `screenshots.sh` konvertieren. +- Chromiums eigenes WebP (`canvas.toDataURL('image/webp')`) nicht verwenden: verlustfrei wird es größer als die PNG. + ## Release-Ablauf **Committen** ist jederzeit erlaubt. **Jeder Push ist ein Release**, also mit Version und CHANGELOG-Eintrag. diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d9c55f..7220df3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V ## [Unreleased] +## [0.26.0] - 2026-10-31 + +### Added +- Verbindungsstatus im Kopf: Ist der Server nicht erreichbar, steht am Desktop unter deinem Namen klein „● offline“ mit rotem Punkt; die Seite bleibt bedienbar. Am Handy zeigt ein kleiner Punkt unten rechts am Profilbild den Status (grün online, rot offline). Das funktioniert auch, wenn nur der Server ausgefallen ist und das Gerät selbst noch Netz hat – bisher war das nur in den Listen zu sehen. + +### Changed +- Screenshots im README sind jetzt kleinere WebP-Bilder (zusammen etwa 0,5 statt 2,7 MB); das Journal-Bild zeigt die aktuelle Ansicht. + ## [0.25.1] - 2026-10-31 ### Fixed @@ -519,7 +527,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.25.1...dev +[Unreleased]: https://git.dsit.me/sandrodach/MindSift/compare/v0.26.0...dev +[0.26.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.25.1...v0.26.0 [0.25.1]: https://git.dsit.me/sandrodach/MindSift/compare/v0.25.0...v0.25.1 [0.25.0]: https://git.dsit.me/sandrodach/MindSift/compare/v0.24.2...v0.25.0 [0.24.2]: https://git.dsit.me/sandrodach/MindSift/compare/v0.24.1...v0.24.2 diff --git a/Doku/architecture.md b/Doku/architecture.md index 49d1c6a..854d3c3 100644 --- a/Doku/architecture.md +++ b/Doku/architecture.md @@ -162,6 +162,7 @@ Die Tests laufen beim Image-Build (Frontend: `npm run test:run`, Backend: `go te - **Lese-Cache** ([lib/offline.ts](../frontend/src/lib/offline.ts)): Jede erfolgreiche GET-Antwort landet pro Benutzer in IndexedDB (`mindsift-offline`). Ohne Verbindung liefert `request()` die letzte bekannte Antwort; so startet die App offline angemeldet und zeigt Gedanken, Aufgaben und Listen. Ausgenommen: Streams, Long-Polling, Notiz-Sync (eigener Speicher, [modules/collaboration.md](modules/collaboration.md)), Export, Suche. Abmelden leert Cache und Warteschlange. - **Warteschlange (Outbox):** Offline erfasste Gedanken (`POST /inbox`) und abgehakte Aufgaben bzw. Listeneinträge (`PATCH`) werden in `localStorage` gesammelt, wiederholtes Umschalten zählt nur mit dem Endstand. Gesendet wird in Reihenfolge beim `online`-Ereignis, beim Start und alle 30 s, solange etwas wartet; vom Server abgelehnte Einträge (4xx) entfallen. Andere Änderungen brauchen weiterhin eine Verbindung. - **Hinweis:** Ein Banner über der unteren Navigation zeigt „Offline“ und die Zahl wartender Änderungen bzw. „Synchronisiere…“. +- **Verbindungsstatus im Kopf** ([lib/useServerOnline.ts](../frontend/src/lib/useServerOnline.ts)): Maßgeblich ist die app-weite Echtzeit-Verbindung (WebSocket/SSE), nicht `navigator.onLine`; so fällt auch ein ausgefallener Server auf. Offline gilt erst nach 4 s ohne Verbindung (Start, kurze Reconnects), ohne Gerätenetz sofort. Desktop: „● offline“ unter dem Namen, nur ohne Verbindung; Handy: Punkt am Profilbild, grün oder rot. ## App-Icons, PWA & Social-Preview (Open Graph) diff --git a/README.md b/README.md index 37f3f4f..8398b63 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,12 @@ [![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.25.1-16a34a?style=flat-square)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/Version-v0.26.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.

- MindSift Triage am Desktop, links hell, rechts dunkel + MindSift Triage am Desktop, links hell, rechts dunkel

Links das helle, rechts das dunkle Theme. @@ -43,9 +43,9 @@ Die meisten Notiz- und Aufgaben-Apps verlangen Struktur im falschen Moment: Wenn * Papierkorb mit Wiederherstellen.

- MindSift Triage am Smartphone, helles Theme + MindSift Triage am Smartphone, helles Theme    - MindSift Notiz am Smartphone, dunkles Theme + MindSift Notiz am Smartphone, dunkles Theme

### ✅ Aufgaben & Kanban @@ -61,7 +61,7 @@ Die meisten Notiz- und Aufgaben-Apps verlangen Struktur im falschen Moment: Wenn * **Alarme & Benachrichtigungen:** Fälligkeits-Alarme sofort, alle übrigen Ereignisse (Freigaben, erledigte geteilte Aufgaben, beobachtete Aufgaben) gebündelt; pro Ereignis wählbar über E-Mail, Discord, eigenen Webhook und Push.

- MindSift Aufgaben und Kanban-Board + MindSift Aufgaben und Kanban-Board

### 📝 Workspace: Outliner-Notizen @@ -88,11 +88,11 @@ Die meisten Notiz- und Aufgaben-Apps verlangen Struktur im falschen Moment: Wenn Technik dahinter: [Doku/modules/collaboration.md](Doku/modules/collaboration.md).

- MindSift Workspace: zwei Personen in derselben Notiz + MindSift Workspace: zwei Personen in derselben Notiz

- MindSift Journal mit Kalender + MindSift Journal mit Kalender

### 🛒 Geteilte Listen @@ -101,7 +101,7 @@ Technik dahinter: [Doku/modules/collaboration.md](Doku/modules/collaboration.md) * Freigabe an Benutzer und Gruppen (Bearbeiter oder Leser); abgehakte Einträge erscheinen bei allen in Echtzeit.

- MindSift Geteilte Listen + MindSift Geteilte Listen

### 🏢 Benutzer, Gruppen, Administration @@ -117,12 +117,12 @@ Technik dahinter: [Doku/modules/collaboration.md](Doku/modules/collaboration.md) - - + + - - + +
Farbfamilie Fjord, links hell, rechts dunkel
Fjord
Farbfamilie Terrakotta, links hell, rechts dunkel
Terrakotta
Farbfamilie Fjord, links hell, rechts dunkel
Fjord
Farbfamilie Terrakotta, links hell, rechts dunkel
Terrakotta
Farbfamilie Polarized, links hell, rechts dunkel
Polarized
Farbfamilie Suri, links hell, rechts dunkel
Suri
Farbfamilie Polarized, links hell, rechts dunkel
Polarized
Farbfamilie Suri, links hell, rechts dunkel
Suri
diff --git a/e2e/README.md b/e2e/README.md index 28a892f..1571c4a 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -40,4 +40,4 @@ Der Eigentümer tippt mit normaler Verbindung, der Bearbeiter sitzt hinter einem ## Screenshots -`e2e/screenshots.sh` baut das Image, startet es im Demo-Modus mit frischer Datenbank und erzeugt die Bilder in `screenshots/` neu (`screenshots.mjs`). Für die Versionsangabe im Kopf: `APP_VERSION=v0.2 e2e/screenshots.sh`. In der Workspace-Aufnahme bearbeitet der Demo-Benutzer `user` gleichzeitig einen Bullet, damit Sperre und Präsenz zu sehen sind. +`e2e/screenshots.sh` baut das Image, startet es im Demo-Modus mit frischer Datenbank und erzeugt die Bilder in `screenshots/` neu (`screenshots.mjs`). Danach werden sie verkleinert (Desktop 1280 px, Handy 480 px breit) und als WebP mit Qualität 90 gespeichert (`cwebp` in einem Alpine-Container). Für die Versionsangabe im Kopf: `APP_VERSION=v0.2 e2e/screenshots.sh`. In der Workspace-Aufnahme bearbeitet der Demo-Benutzer `user` gleichzeitig einen Bullet, damit Sperre und Präsenz zu sehen sind. diff --git a/e2e/screenshots.mjs b/e2e/screenshots.mjs index aa9c590..bd00eb0 100644 --- a/e2e/screenshots.mjs +++ b/e2e/screenshots.mjs @@ -196,7 +196,10 @@ const main = async () => { await page.getByRole('button', { name: 'Heute' }).click().catch(() => {}); await page.mouse.click(1000, 860); await sleep(2500); + // Round corners (setting "Ecken: rund") for this picture only + await page.evaluate(() => document.documentElement.setAttribute('data-corners', 'round')); await plain(page, 'journal_desktop.png', 'dark'); + await page.evaluate(() => document.documentElement.removeAttribute('data-corners')); await page.getByRole('button', { name: 'Journal', exact: true }).click(); // close it again await tab(page, 'Listen'); diff --git a/e2e/screenshots.sh b/e2e/screenshots.sh index 0818b7f..2b5da39 100755 --- a/e2e/screenshots.sh +++ b/e2e/screenshots.sh @@ -1,6 +1,7 @@ #!/usr/bin/env bash # Builds MindSift, starts it in demo mode with a fresh database and renews the README screenshots # in screenshots/ (title picture and colour-family grid: light and dark joined along a diagonal). +# The PNGs from the browser are scaled down (desktop 1280 px, phone 480 px wide) and saved as WebP. # Requires podman (or docker via ENGINE=docker). Usage: e2e/screenshots.sh, other theme pair: SHOT_LIGHT=fjord SHOT_DARK=fjord-night set -euo pipefail ENGINE="${ENGINE:-podman}" @@ -21,6 +22,7 @@ cleanup() { rm -rf "$WORK" } trap cleanup EXIT +mkdir -p "$WORK/png" echo "Baue Image …" # Version in the header, e.g. APP_VERSION=v0.2 e2e/screenshots.sh @@ -32,5 +34,14 @@ for _ in $(seq 1 60); do curl -s -m 1 "http://localhost:$PORT/api/v1/health" >/d echo "Erstelle Screenshots …" "$ENGINE" run --rm --network host -e MINDSIFT_URL="http://localhost:$PORT" -e SHOTS_DIR=/shots \ -e SHOT_LIGHT="${SHOT_LIGHT:-light}" -e SHOT_DARK="${SHOT_DARK:-dark}" -e LANG=de_DE.UTF-8 -e TZ=Europe/Berlin \ - -v "$ROOT/e2e":/e2e:Z -v "$ROOT/screenshots":/shots:Z -w /e2e mcr.microsoft.com/playwright:v1.48.2-jammy \ + -v "$ROOT/e2e":/e2e:Z -v "$WORK/png":/shots:Z -w /e2e mcr.microsoft.com/playwright:v1.48.2-jammy \ sh -c 'npm install --no-audit --no-fund >/dev/null 2>&1 && node screenshots.mjs' + +echo "Wandle in WebP um …" +"$ENGINE" run --rm -v "$WORK/png":/png:Z -v "$ROOT/screenshots":/shots:Z docker.io/library/alpine:3.20 sh -c ' + apk add -q libwebp-tools >/dev/null + for f in /png/*.png; do + name=$(basename "$f" .png) + case "$name" in Mobile_*) width=480 ;; *) width=1280 ;; esac + cwebp -quiet -resize "$width" 0 -q 90 -m 6 -metadata none "$f" -o "/shots/$name.webp" + done' diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 6069a46..c238685 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "mindsift-frontend", - "version": "0.25.1", + "version": "0.26.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "mindsift-frontend", - "version": "0.25.1", + "version": "0.26.0", "license": "Unlicense", "dependencies": { "@fontsource-variable/inter": "5.3.0", diff --git a/frontend/package.json b/frontend/package.json index c4df013..68cfb16 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "mindsift-frontend", "private": true, - "version": "0.25.1", + "version": "0.26.0", "license": "Unlicense", "type": "module", "scripts": { diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 358a61c..6112909 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -56,6 +56,7 @@ import { WorkspaceView } from './features/workspace/WorkspaceView'; import { SharedListsView } from './features/lists/SharedListsView'; import { SetupAndAuthView } from './features/auth/SetupAndAuthView'; import { OfflineBanner } from './components/OfflineBanner'; +import { useServerOnline } from './lib/useServerOnline'; import { SharedImportToast } from './components/SharedImportToast'; import { outboxCount, clearOfflineData } from './lib/offline'; import { DemoCountdown } from './components/DemoCountdown'; @@ -179,6 +180,7 @@ export const App: React.FC = () => { // Profile Dropdown State const [showProfileMenu, setShowProfileMenu] = useState(false); + const serverOnline = useServerOnline(); const [showAbout, setShowAbout] = useState(false); const dropdownRef = useRef(null); @@ -947,7 +949,17 @@ export const App: React.FC = () => { }`} title="Profil & Einstellungen" > - + + + {/* Connection status like in a chat app: green online, red without server */} + + @@ -1013,8 +1025,20 @@ export const App: React.FC = () => { title="Benutzermenü öffnen" > - - {user.displayName} + + + {user.displayName} + + {!serverOnline && ( + + + offline + + )} diff --git a/frontend/src/lib/__tests__/useServerOnline.test.tsx b/frontend/src/lib/__tests__/useServerOnline.test.tsx new file mode 100644 index 0000000..87764f8 --- /dev/null +++ b/frontend/src/lib/__tests__/useServerOnline.test.tsx @@ -0,0 +1,66 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { act, renderHook } from '@testing-library/react'; + +let statusListener: ((connected: boolean) => void) | null = null; +let currentConnected = false; +vi.mock('../api', () => ({ + onRealtimeStatus: (cb: (connected: boolean) => void) => { + statusListener = cb; + cb(currentConnected); + return () => (statusListener = null); + }, +})); + +import { useServerOnline, SERVER_OFFLINE_GRACE_MS } from '../useServerOnline'; + +function setDeviceOnline(value: boolean) { + Object.defineProperty(navigator, 'onLine', { value, configurable: true }); +} + +describe('useServerOnline', () => { + beforeEach(() => { + vi.useFakeTimers(); + currentConnected = false; + setDeviceOnline(true); + }); + afterEach(() => vi.useRealTimers()); + + it('stays online during the grace period at app start', () => { + const { result } = renderHook(() => useServerOnline()); + expect(result.current).toBe(true); + act(() => statusListener?.(true)); + act(() => vi.advanceTimersByTime(SERVER_OFFLINE_GRACE_MS + 100)); + expect(result.current).toBe(true); + }); + + it('turns offline once the server stays unreachable and back online on reconnect', () => { + currentConnected = true; + const { result } = renderHook(() => useServerOnline()); + act(() => statusListener?.(false)); + expect(result.current).toBe(true); + act(() => vi.advanceTimersByTime(SERVER_OFFLINE_GRACE_MS + 100)); + expect(result.current).toBe(false); + act(() => statusListener?.(true)); + expect(result.current).toBe(true); + }); + + it('repeated failed reconnects do not postpone the offline state', () => { + currentConnected = true; + const { result } = renderHook(() => useServerOnline()); + for (let i = 0; i < 4; i++) { + act(() => statusListener?.(false)); + act(() => vi.advanceTimersByTime(SERVER_OFFLINE_GRACE_MS / 2)); + } + expect(result.current).toBe(false); + }); + + it('is offline right away without device network', () => { + currentConnected = true; + const { result } = renderHook(() => useServerOnline()); + act(() => { + setDeviceOnline(false); + statusListener?.(false); + }); + expect(result.current).toBe(false); + }); +}); diff --git a/frontend/src/lib/useServerOnline.ts b/frontend/src/lib/useServerOnline.ts new file mode 100644 index 0000000..16d710b --- /dev/null +++ b/frontend/src/lib/useServerOnline.ts @@ -0,0 +1,45 @@ +import { useEffect, useState } from 'react'; +import { onRealtimeStatus } from './api'; + +// Grace period before a missing realtime connection counts as offline (app start, short reconnects) +export const SERVER_OFFLINE_GRACE_MS = 4000; + +// Whether the server is reachable, judged by the app-wide realtime connection (WebSocket/SSE). Unlike +// navigator.onLine it also notices a server that is down while the device still has network. Without +// device network it is offline right away; otherwise only after the connection stayed away for a moment. +export function useServerOnline(): boolean { + const [online, setOnline] = useState(true); + + useEffect(() => { + let connected = false; + let timer: number | undefined; + // Reconnect attempts report "not connected" every few seconds; they must not restart the grace period + const update = () => { + if (connected) { + window.clearTimeout(timer); + timer = undefined; + setOnline(true); + } else if (navigator.onLine === false) { + window.clearTimeout(timer); + timer = undefined; + setOnline(false); + } else if (timer === undefined) { + timer = window.setTimeout(() => setOnline(false), SERVER_OFFLINE_GRACE_MS); + } + }; + const unsubscribe = onRealtimeStatus((isConnected) => { + connected = isConnected; + update(); + }); + window.addEventListener('online', update); + window.addEventListener('offline', update); + return () => { + unsubscribe(); + window.clearTimeout(timer); + window.removeEventListener('online', update); + window.removeEventListener('offline', update); + }; + }, []); + + return online; +} diff --git a/screenshots/Listen_desktop.png b/screenshots/Listen_desktop.png deleted file mode 100644 index a710cf9..0000000 Binary files a/screenshots/Listen_desktop.png and /dev/null differ diff --git a/screenshots/Listen_desktop.webp b/screenshots/Listen_desktop.webp new file mode 100644 index 0000000..859100b Binary files /dev/null and b/screenshots/Listen_desktop.webp differ diff --git a/screenshots/Mobile_Triage.png b/screenshots/Mobile_Triage.png deleted file mode 100644 index f4a0fbc..0000000 Binary files a/screenshots/Mobile_Triage.png and /dev/null differ diff --git a/screenshots/Mobile_Triage.webp b/screenshots/Mobile_Triage.webp new file mode 100644 index 0000000..24e9d10 Binary files /dev/null and b/screenshots/Mobile_Triage.webp differ diff --git a/screenshots/Mobile_Workspace.png b/screenshots/Mobile_Workspace.png deleted file mode 100644 index 4eb91f3..0000000 Binary files a/screenshots/Mobile_Workspace.png and /dev/null differ diff --git a/screenshots/Mobile_Workspace.webp b/screenshots/Mobile_Workspace.webp new file mode 100644 index 0000000..9d3b838 Binary files /dev/null and b/screenshots/Mobile_Workspace.webp differ diff --git a/screenshots/Theme_fjord.png b/screenshots/Theme_fjord.png deleted file mode 100644 index 619d1e6..0000000 Binary files a/screenshots/Theme_fjord.png and /dev/null differ diff --git a/screenshots/Theme_fjord.webp b/screenshots/Theme_fjord.webp new file mode 100644 index 0000000..461ecdd Binary files /dev/null and b/screenshots/Theme_fjord.webp differ diff --git a/screenshots/Theme_polarized.png b/screenshots/Theme_polarized.png deleted file mode 100644 index 6b679e8..0000000 Binary files a/screenshots/Theme_polarized.png and /dev/null differ diff --git a/screenshots/Theme_polarized.webp b/screenshots/Theme_polarized.webp new file mode 100644 index 0000000..9c2c8eb Binary files /dev/null and b/screenshots/Theme_polarized.webp differ diff --git a/screenshots/Theme_suri.png b/screenshots/Theme_suri.png deleted file mode 100644 index 95735b4..0000000 Binary files a/screenshots/Theme_suri.png and /dev/null differ diff --git a/screenshots/Theme_suri.webp b/screenshots/Theme_suri.webp new file mode 100644 index 0000000..c9d14fa Binary files /dev/null and b/screenshots/Theme_suri.webp differ diff --git a/screenshots/Theme_terracotta.png b/screenshots/Theme_terracotta.png deleted file mode 100644 index 6fa4fab..0000000 Binary files a/screenshots/Theme_terracotta.png and /dev/null differ diff --git a/screenshots/Theme_terracotta.webp b/screenshots/Theme_terracotta.webp new file mode 100644 index 0000000..b572953 Binary files /dev/null and b/screenshots/Theme_terracotta.webp differ diff --git a/screenshots/Triage_desktop.png b/screenshots/Triage_desktop.png deleted file mode 100644 index 8b5bc85..0000000 Binary files a/screenshots/Triage_desktop.png and /dev/null differ diff --git a/screenshots/Triage_desktop.webp b/screenshots/Triage_desktop.webp new file mode 100644 index 0000000..461d7a6 Binary files /dev/null and b/screenshots/Triage_desktop.webp differ diff --git a/screenshots/aufgaben_desktop.png b/screenshots/aufgaben_desktop.png deleted file mode 100644 index 14fa99f..0000000 Binary files a/screenshots/aufgaben_desktop.png and /dev/null differ diff --git a/screenshots/aufgaben_desktop.webp b/screenshots/aufgaben_desktop.webp new file mode 100644 index 0000000..412e2cc Binary files /dev/null and b/screenshots/aufgaben_desktop.webp differ diff --git a/screenshots/journal_desktop.png b/screenshots/journal_desktop.png deleted file mode 100644 index 7beb4fa..0000000 Binary files a/screenshots/journal_desktop.png and /dev/null differ diff --git a/screenshots/journal_desktop.webp b/screenshots/journal_desktop.webp new file mode 100644 index 0000000..db809dd Binary files /dev/null and b/screenshots/journal_desktop.webp differ diff --git a/screenshots/workspace_desktop.png b/screenshots/workspace_desktop.png deleted file mode 100644 index 1363ec5..0000000 Binary files a/screenshots/workspace_desktop.png and /dev/null differ diff --git a/screenshots/workspace_desktop.webp b/screenshots/workspace_desktop.webp new file mode 100644 index 0000000..1f6eec8 Binary files /dev/null and b/screenshots/workspace_desktop.webp differ