Commit Graph
16 Commits
Author SHA1 Message Date
marcandClaude Opus 5 79a7cc6a3d feat(calendar): Wiederholungen, Tagesansicht, Terminfarben, Farbpunkt als Schalter
Fünf Wünsche aus dem Durchtesten:

1. Wiederholungen einstellbar (täglich/wöchentlich/monatlich/jährlich,
   alle N, Ende nie/nach Anzahl/an einem Datum). Der eigentliche Aufwand
   lag nicht im Formular, sondern im Aufklappen: eine wöchentliche Reihe
   erschien sonst als *ein* Eintrag. Der Server klappt sie jetzt im
   angefragten Zeitfenster auf (?from=&to=, rrule-go über go-icals
   RecurrenceSet), die Ansichten fragen ihren sichtbaren Ausschnitt an.
   Geprüft: 5 Termine wöchentlich, nach Umstellung auf 14-tägig korrekt
   neu gerechnet, Fenster einer Woche liefert genau einen.

   Regeln, die das Formular nicht abbildet (etwa jeder zweite Dienstag
   im Monat), werden weiterhin korrekt *angezeigt* -- geprüft, die vier
   Termine stimmen -- aber nicht zum Bearbeiten angeboten (409). Ein
   halb verstandenes Speichern würde die Regel sonst still beschneiden.

2. Tag wird erst geöffnet, statt sofort einen Termin anzulegen: Klick
   auf einen Tag zeigt dessen Termine als Liste, dort wählt man einen
   aus oder legt bewusst einen neuen an.

3. Eigene Farbe je Termin (COLOR nach RFC 7986), leer = Kalenderfarbe.

4. Kalenderliste ohne Kästchen: der Farbpunkt selbst ist der Schalter --
   gefüllt heißt sichtbar, leerer Ring ausgeblendet.

5. Ganztägig als Schiebeschalter statt Kästchen -- es stellt das
   Formular um, das ist keine Nebenangabe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 00:56:28 +02:00
marcandClaude Opus 5 105ce8de4a fix(calendar): Termine anklickbar, Ganztags-Datum korrekt, Wochenraster mit Stunden
Drei gemeldete Fehler, alle nachgestellt:

1. Ein vorhandener Termin ließ sich nicht öffnen. Der Klick auf den
   Termin reichte an die Tageszelle darunter durch, die sofort 'Neuer
   Termin' öffnete und den Bearbeiten-Dialog überschrieb. Fehlendes
   stopPropagation.

2. Klick auf den 3. legte den Termin am 2. an. Ein ganztägiger Termin
   ist ein *Datum*, kein Zeitpunkt - die Oberfläche rechnete ihn über
   new Date(...).toISOString() trotzdem in UTC um, was östlich von UTC
   den Vortag ergibt. Nachgestellt und bestätigt: DTSTART landete als
   20260902 statt 20260903. Ganztägige Termine laufen jetzt über
   dates.ts (allDayISO/eventDay), damit Dialog und Raster dieselbe
   Rechnung benutzen. Nebenbefund mitgefixt: DTEND war gleich DTSTART,
   was RFC 5545 verbietet (DTEND ist exklusiv) - ein eintägiger Termin
   am 3. schreibt jetzt DTEND 20260904, nach außen bleibt die
   Schnittstelle beim letzten belegten Tag.

3. 'Fühlt sich starr an': Wochenansicht ist jetzt ein echtes
   Stundenraster (Klick trifft die Stunde statt Mitternacht, ganztägige
   in eigener Zeile, überlappende Termine nebeneinander, Jetzt-Linie),
   mehrtägige Termine stehen in jeder Zelle statt nur am Starttag, neue
   Termine schlagen die nächste volle Stunde bzw. 9 Uhr vor,
   Kalenderauswahl im Termindialog, Escape schließt, Hover-Rückmeldung,
   Kalenderliste auf schmalen Schirmen einklappbar.

Außerdem: ein neu angelegter Kalender blieb unsichtbar, weil er in
keiner Sichtbar-Liste stand - die Liste merkt sich jetzt die
ausgeblendeten statt der sichtbaren.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 23:47:08 +02:00
marcandClaude Sonnet 5 ba33706d2e fix(calendar): Symbol in der Kopfleiste ergänzt
Jede andere Seite zeigt neben dem Titel ein Strichsymbol (Papierkorb,
Geteiltes, Etiketten, ...) - beim Kalender fehlte es, PageHeader bekam
keinen icon-Prop. Dasselbe Kalendersymbol wie in der Seitenleiste.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 23:36:21 +02:00
marcandClaude Sonnet 5 27a026421f feat(calendar): Phase 3 – Weboberfläche mit Monats-, Wochen- und Listenansicht
Neue Seite CalendarPage (Menüpunkt 'Calendar' in der Seitenleiste,
zwischen Home und Papierkorb) mit Kalenderliste zum Anlegen/Umbenennen/
Löschen/Ein- und Ausblenden, Export/Import als .ics über die
Phase-2-API, und Termine anlegen/ändern/löschen über einen neuen
JSON-Endpunkt.

Backend: neuer events_api.go-Handler unter /api/v1/calendars/{id}/events
(Session-Auth) neben dem bestehenden CalDAV-Protokoll – ein Termin, den
die Web-Oberfläche anlegt, ist ein ganz normales CalDAV-Objekt und
synchronisiert genauso zu DAVx5 wie umgekehrt. Ein per CalDAV mit RRULE
angelegter, wiederkehrender Termin zeigt sich hier nur (recurring:true),
lässt sich aber bewusst nicht über dieses vereinfachte Formular
bearbeiten (409) – das würde die Wiederholungsregel eher beschädigen
als korrekt anpassen.

Gegen den laufenden Container getestet: Kalender/Termin anlegen, per
Web-API ändern und löschen, ein per CalDAV mit RRULE angelegter
Wiederkehrer taucht als recurring:true auf und blockt PATCH mit 409.
TypeScript-Build (tsc + vite build) sauber.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 23:14:28 +02:00
marcandClaude Opus 5 9d152e3677 refactor: App-Umschalter und HONEYBAR_KEY entfernt
Beide waren Ballast, aber aus verschiedenen Gründen.

Der App-Umschalter (EXTERNAL_APPS, AppSwitcher.tsx) hing seit dem
30.07.2026 nirgends mehr in der Oberfläche – auf Marcs Wunsch damals aus
der Seitenleiste genommen, "weil eine gemeinsame Leiste für alle Apps
noch überlegt wird". Diese Leiste gibt es inzwischen: die HoneyBar. Das
Backend las die Variable trotzdem weiter und lieferte external_apps in
der öffentlichen Konfiguration aus. Weg damit, samt Typ und Parser.

HONEYBAR_KEY war der Ausweis für die App-Liste, wenn HoneyOS auf einer
fremden Site liegt. Bei Marc liegen honeyos.duckdns.org und
cloud.honeyos.duckdns.org unter derselben Site, das Sitzungscookie fließt
also mit. Nachgesehen, wie HoneyOS entscheidet: Es prüft zuerst die
Sitzung (SubFromRequest) und greift erst danach zum Schlüssel – die
Leiste kommt hier ohne ihn an ihre Liste. Gedacht war er für Widgets;
die sind vertagt.

Aether schickt damit kein data-key mehr; HoneyOS behält seine Seite der
Prüfung, für den Fall, dass später doch eine App auf fremder Domain dazu
kommt.

Nachgeprüft: go build, go vet und die Tests laufen; das Abbild baut ohne
TypeScript-Fehler; die öffentliche Konfiguration liefert weder
honeybar_key noch external_apps; im gebauten Bundle kommt data-key nicht
mehr vor.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:15:28 +02:00
marcandClaude Opus 5 6c53651568 fix(office): PDF am Telefon nur bei OnlyOffice, Collabora behält den Editor
Die Regel galt für beide Anbieter – das war falsch. Marcs Kritik betraf
nur OnlyOffice: Es baut auf dem Telefon dieselbe Oberfläche auf wie am
PC. Collabora bringt für kleine Schirme eine eigene Ansicht mit, die
taugt; dort wäre der Umweg über ein PDF ein Rückschritt.

Der Anbieter steht jetzt in der öffentlichen Konfiguration
(office_provider), und die Oberfläche entscheidet daran. Der Endpunkt
selbst kann weiterhin beides.

Nachgeholt: Der ganze Weg ist jetzt auch mit Collabora gemessen, nicht
nur mit OnlyOffice – gegen die Dev-Instanz, die damit läuft.
  erster Abruf 0,347 s, zweiter 0,009 s, eine Datei auf der Platte
  (OnlyOffice zum Vergleich: 1,05 s – es muss die Datei erst abholen)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-01 01:07:08 +02:00
marcandClaude Opus 5 9c5cda75d7 feat(office): Dokumente am Telefon als PDF ansehen statt bearbeiten
Am Finger öffnet ein Office-Dokument jetzt das gewandelte PDF im eigenen
Betrachter; "Edit" daneben führt trotzdem in den Editor. Mit Maus ändert
sich nichts.

Der Web-Editor ist eine vollständige Textverarbeitung – zwischen seinen
Leisten bleibt auf 400 Pixeln vom Dokument ein Rechteck. Collabora und
OnlyOffice verhalten sich gleich; deshalb kein besserer Bedienmodus,
sondern ein anderes Werkzeug. Gewandelt wird von dem Server, der auch
bearbeitet: Sonst sähe man unterwegs ein LibreOffice-Layout eines
Dokuments, für das man OnlyOffice gewählt hat.

Je Dokument gibt es GENAU EIN PDF, und das ist keine Frage des
Aufräumens, sondern des Schlüssels: previews/<id>.pdf trägt die Fassung
nicht im Namen, eine neue Fassung überschreibt. files.preview_version
sagt, ob das Abgelegte noch passt.

Sicherheit: Die Adresse, die OnlyOffice zurückgibt, wird von uns
abgerufen – der Wähler des Transports lässt nur OFFICE_ALLOWED_HOSTS zu,
zusätzlich eine ausdrückliche Prüfung mit Test. JWT-Signatur (HS256) für
die eigene API des Dokumentenservers. Fremde Kennung 404, ohne Anmeldung
401. Das Ergebnis muss mit %PDF- beginnen, sonst landete eine
Fehlerseite im Zwischenspeicher.

Gemessen gegen einen echten OnlyOffice-Server im Docker-Netz:
  erster Abruf 1,05 s -> zweiter 0,009 s
  erneut hochgeladen -> Fassung 2, preview_version 2, weiterhin 1 Datei
  GC: herrenloses PDF von vorgestern weg, echtes bleibt
  endgültig gelöscht -> PDF weg; Papierkorb -> PDF bleibt
  ohne Anmeldung 401, fremde Kennung 404, Nicht-Office 415

Dazu im Admin-Bereich neu: Platzbedarf der abgeleiteten Daten
(Vorschaubilder, PDFs, Profilbilder). Sie zählen gegen kein Kontingent –
ein Ordner, der wächst, ohne dass ihn jemand sieht, ist eine stille
Überraschung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-31 23:30:49 +02:00
marcandClaude Opus 5 5ea66ea884 fix(thumbnails): Fassung in der Adresse, Sofortanzeige, kein Flackern
Gleicher Dateiname erzeugt keine neue Datei, sondern eine neue Fassung -
gleiche ID, gleiche Vorschaubild-Adresse. Die darf einen Tag im Browser
liegen, also zeigte die Kachel nach dem erneuten Hochladen bis zu 24
Stunden das alte Bild. Das erklaert, warum eine Drehung erst nach
mehreren Versuchen "ankam". Die Adresse traegt jetzt ?v=<Fassung>.

Vorschaubilder entstehen im Hintergrund; die Kachel fragt jetzt bis zu
fuenfmal nach, statt bis zum naechsten Neuladen ihr Ersatzsymbol zu
zeigen. Nur bei Bildern juenger als zwei Minuten - sonst fragte jede
dauerhaft fehlgeschlagene Datei bei jedem Oeffnen erneut. Gemessen: 608
ms bis zur Anzeige, ohne Neuladen.

Das Anheben der Kachel beim Ueberfahren ist raus: Steht der Zeiger auf
der Unterkante, rutscht sie unter ihm weg, mouseleave, sie faellt
zurueck, mouseenter - im Kreis, sichtbar als Flackern.

Dazu liest die EXIF-Auswertung jetzt 256 statt 64 KB und gibt bei einem
abgeschnittenen Block nicht mehr auf: Die Drehung steht vorn, das
eingebettete Vorschaubild dahinter.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 14:29:09 +02:00
marcandClaude Opus 5 5b3658640e revert(admin): Knopf zum Neuerzeugen der Vorschaubilder wieder raus
130 Zeilen fuer einen einmaligen Bedarf - die Haelfte davon eine Karte
im Admin-Bereich, die sonst dauerhaft dort steht. Die EXIF-Behebung
selbst bleibt: Neu hochgeladene Bilder stehen richtig herum.

Bestehende Vorschaubilder werden damit nicht mehr nachgezogen. Wer sie
gerade ruecken will, laedt die Datei erneut hoch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 14:15:00 +02:00
marcandClaude Opus 5 4bd9a6ca56 fix(thumbnails): EXIF-Drehung auswerten, hochkant blieb quer
Kameras speichern die Bildpunkte fast immer quer und legen die Drehung
als EXIF-Notiz daneben. Browser befolgen sie von selbst - Go nicht.
Deshalb stand dasselbe Foto in der grossen Ansicht aufrecht und lag im
Vorschaubild auf der Seite.

Neues Paket internal/imageutil liest genau ein Feld (0x0112) aus dem
Anfang der Datei und dreht danach. Von Hand statt mit einer Bibliothek:
Der Weg dorthin ist kurz und liegt fest, und gebraucht wird ein Feld.
Gelesen werden nur die ersten 64 KB, damit ein 40-MB-Foto nicht zweimal
im Speicher liegt.

Gedreht wird vor dem Verkleinern - andersherum bekaeme ein hochkantes
Foto den Zuschnitt eines Querformats.

Bestehende Vorschaubilder aendern sich davon nicht. Dafuer neu:
POST /admin/thumbnails/rebuild samt Knopf im Admin-Bereich.

Auch Profilbilder aus OIDC laufen jetzt durch dieselbe Auswertung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 14:08:10 +02:00
marcandClaude Opus 5 d6b9d6a1ce fix(ui): die Seite rechnet die Höhe der HoneyOS-Leiste ab
Die Leiste liegt fest am oberen Rand und schiebt den Inhalt herunter. Wer
danach noch mit 100vh rechnete, war genau um ihre Höhe zu hoch — am Rechner
eine Bildlaufleiste, die es vorher nicht gab. Und wer selbst fest am oberen
Rand klebte (Seitenleiste am Handy, Vorschau, Office), lag darunter und war
verdeckt.

--honeybar-h setzt die Leiste selbst auf <html>; ohne sie ist der Wert 0 und
alles bleibt wie zuvor. Drei Hilfsklassen in index.css halten das an einer
Stelle zusammen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 23:40:34 +02:00
marcandClaude Opus 5 1d933c5c73 feat(files): Sammel-Loeschen und Ziehen auf den Papierkorb
Zwei Wege, weil sie verschiedene Momente treffen: Der Knopf in der
Auswahlleiste raeumt eine geplante Auswahl weg, das Ziehen auf den
Papierkorb in der Seitenleiste die eine Datei, die einem gerade
auffaellt.

Zieht man eine markierte Datei, meint man die Markierung - deshalb
traegt der Ziehvorgang jetzt zusaetzlich aether-file-ids mit allen
markierten IDs. Eine unmarkierte Datei zieht nur sich selbst.

Der Knopf fragt nach, das Ziehen nicht: Ziehen ist eine bewusste
Bewegung mit gedrueckter Taste, und der Papierkorb ist umkehrbar. Eine
Rueckfrage nach jedem Zug klickt man nach dem dritten Mal blind weg.

filesApi.deleteMany schickt vier Anfragen gleichzeitig und meldet, was
nicht ging; fehlgeschlagene Dateien bleiben markiert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 22:43:36 +02:00
marcandClaude Opus 5 b5a83e4057 fix(upload): gezogenes Vorschaubild wurde erneut hochgeladen
Zieht man das angezeigte Bild ein Stueck zur Seite, legt Chrome es als
echte Datei in den Ziehvorgang. dataTransfer.types enthaelt dann
"Files" - nicht zu unterscheiden von einer Datei aus dem Dateimanager.
Die Ablageflaeche lud das eigene Bild daraufhin ein zweites Mal hoch.

Zwei Riegel: draggable={false} an allen Bildern, die kein eigenes
Ziehen brauchen (die Kachel bleibt ziehbar und verschiebt die Datei),
und useInternalDrag - was in dieser Seite losgezogen ist, wird nie als
Upload behandelt. dragstart feuert nur bei eigener Quelle.

Gemessen: vorher 1 Upload, nachher 0. Ein Zug von aussen laedt
unveraendert hoch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 17:24:22 +02:00
marcandClaude Opus 5 e9e07f9bed fix(ui): schmale Geraete zeigten nur eine Kachelspalte
Auf 360px (Galaxy Z Flip 7 aufgeklappt) brauchten zwei Spalten
2x160 + 12 = 332px, verfuegbar waren nach 20px Innenrand aber nur
320px - also fiel das Raster auf eine Spalte zurueck.

Schmal jetzt 132px Mindestbreite, 10px Abstand, 12px Innenrand. Damit
stehen zwei Spalten ab 300px Fensterbreite, ohne dass breite Schirme
sich aendern (dort weiter 160px und 20px).

Raster und Innenrand stehen als Klassen in index.css: inline gesetzt
haette die Media-Query sie nicht erreicht - derselbe Fehler wie zuletzt
in der Kopfleiste.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 16:11:27 +02:00
HoneyandClaude Opus 5 3019e84681 feat(oidc): OIDC_BUTTON_LABEL, und vier kaputte Zeilen in .env.example
Der Knopf hiess fest "Sign in with SSO / OIDC". Der Anbieter heisst bei
jedem anders; "Anmelden mit PocketID" sagt dem, der davorsteht, mehr.
Vorgabe unveraendert. Der Wert geht nur ueber /public/config, wenn OIDC
ueberhaupt an ist.

Beim Testen dabei selbst einen Fehler gebaut und eingefangen: Ein
Kommentar hinter einem LEEREN Wert wird in einer .env zum Wert.
OIDC_ISSUER= # z. B. https://... startete den OIDC-Aufbau mit dem
Kommentartext als Adresse, und der Start brach ab.

In deploy/.env.example steckten vier weitere solche Zeilen, darunter
OFFICE_PROVIDER= # collabora | onlyoffice - Office waere mit Unsinn als
Anbieter eingeschaltet worden. Alle umgestellt, Warnung an den Dateikopf.

Dazu im README belegt, was beim Ausschalten der Versionierung passiert:
Fassungen bleiben, auch im Papierkorb; erst das Leeren entfernt sie samt
Blobs, und zwar sofort statt erst beim GC.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 13:08:31 +02:00
HoneyandClaude Opus 5 31a027c93a feat(aether): initial commit – self-hosted Cloud, Phasen 1–7
Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA.
Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb,
Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming,
Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office
(Collabora/OnlyOffice).

Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort)
sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 09:25:36 +02:00