Zwei Dinge, die am Telefon fehlten – beide in der installierten App
besonders schmerzhaft, weil es dort keine Adresszeile als Rückweg gibt.
1. Zurück schloss bisher die Anwendung, nicht die Vorschau. Vorschau,
Office-Editor und die aufgeschobene Seitenleiste kamen im Verlauf des
Browsers gar nicht vor: Der einzige Eintrag war die Dateiliste. Jetzt
legt jede bildschirmfüllende Ansicht beim Öffnen einen leeren Eintrag
an (die Adresse bleibt gleich – eine Vorschau ist kein Ort für ein
Lesezeichen) und verbraucht ihn beim Schließen wieder, egal ob über
die Geste, Escape oder den Knopf. Ein Stapel sorgt dafür, dass bei
übereinanderliegenden Ansichten nur die oberste reagiert.
Collabora blendet auf kleinen Schirmen seinen eigenen Schließen-Knopf
aus (closeButtonEnabled && !isSmallScreenDevice) – ohne diesen Weg
käme man aus einem Dokument am Telefon nur über die Kopfzeile heraus.
2. Die Zusammenziehgeste vergrößerte die ganze Seite statt des PDFs:
Leiste und Knöpfe wuchsen mit, das Dokument blieb grob. touch-action
nimmt dem Browser die Geste, ein eigener Zuhörer setzt unsere
Zoomstufen an ihre Stelle, verankert an der Mitte zwischen den
Fingern – dieselbe Regel wie bei Strg+Mausrad.
Gemessen in Chromium mit Touch-Emulation, 412x900:
Vorschau offen -> Verlauf 2->3, Zurück -> Vorschau zu, Anwendung bleibt
Esc -> Eintrag wieder weg (ein weiteres Zurück verlässt die App)
Aufziehen 65% -> 125%, Zuziehen -> 50%, visualViewport.scale bleibt 1
Ein Befund aus dieser Messung steckt im Code: Zuerst kam pro Geste nur
eine Stufe, weil sich der Zuhörer bei jeder Zoomänderung neu anmeldete
und der gemerkte Fingerabstand dabei verlorenging. Abstand und Maßstab
liegen deshalb in Referenzen, der Zuhörer meldet sich einmal an.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nach einem Stapel-Upload blieb unten rechts für jede Datei eine Karte
stehen, bis man sie einzeln wegklickte oder die Seite neu lud. Bei
vielen Dateien war der Bildschirm zu.
Fertige Meldungen räumen sich jetzt nach vier Sekunden selbst ab.
Fehler bleiben: Sie tragen die einzige Auskunft, die es sonst nirgends
gibt. Zusätzlich stehen höchstens vier Karten gleichzeitig, der Rest
wird gezählt – der Bildschirm läuft ja schon während des Hochladens
voll, nicht erst danach. Gescheiterte Dateien stehen dabei oben.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Bisher gab es dafuer nur den Knopf oben links - am Telefon die Ecke,
die man einhaendig am schlechtesten erreicht.
Vier Bedingungen halten die Geste aus dem Weg: nur im 44-px-Streifen am
Rand (sonst waeren waagerecht scrollbare Bereiche nicht mehr zu
bedienen), mindestens 60 px Weg, doppelt so weit waagerecht wie
senkrecht (sonst oeffnet sie beim Blaettern), hoechstens 800 ms.
44 px und nicht 20: Androids Zurueck-Geste belegt die aeussersten ~20
px, was dort beginnt erreicht die Seite nie.
Aus einer bildschirmfuellenden Ansicht heraus tut sie nichts - dort
wischt man durch Seiten, und die Leiste laege ohnehin dahinter.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
Die Zoomknoepfe sitzen jetzt rechts in der Leiste statt links neben der
Seitenzahl - dieselbe Anordnung wie in InkNote. Die dritte Rasterspalte
war ohnehin da, damit die Suche wirklich mittig sitzt; jetzt traegt sie
auch etwas. Schmal bleibt der Zoom in der ersten Zeile, sonst waere die
Leiste am Telefon dreizeilig geworden.
Strg (oder Cmd) + Mausrad zoomt das Dokument. Der Zuhoerer wird von Hand
angemeldet: React meldet Rad-Ereignisse passiv an, und passiv darf man
preventDefault nicht rufen - ohne das zoomt der Browser die ganze Seite
statt des Dokuments.
Beim Rad wird der Punkt unter dem Zeiger festgehalten statt der Seite.
Gemessen: Seite 2 bleibt ueber zwei Zoomstufen unter dem Zeiger.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>
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>
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>
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>
Ein Info-Knopf unter Einstellungen -> Access oeffnet drei Wege:
Windows (Netzlaufwerk), Linux ueber davfs2 samt fstab und secrets, und
Linux ueber den Dateimanager. Dazu WebDAV-Adresse, Benutzername und
Cloud-Adresse zum Kopieren.
Die WebDAV-Adresse stand bisher nirgends in der Oberflaeche - sie ist
zwar ableitbar, aber nur wenn man weiss, dass es sie gibt. Sie entsteht
aus window.location statt aus PUBLIC_URL: Was im Browser traegt, traegt
auch im Dateimanager.
Mitgenommen sind die drei Stellen, an denen es sonst scheitert: Windows
bricht ohne HTTPS ab und laedt nichts ueber 50 MB, die Konsole braucht
davfs2, und Dateimanager wollen davs:// statt https://.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Regeln der schmalen Ansicht griffen nie: Ein Inline-Stil schlägt
jede Klassenregel, und `padding: '0 14px'` samt `paddingTop:
var(--safe-top)` stand genau dort. Gemessen kam padding-top als 0px an
statt als 8px - Titel und Nutzerbereich klebten am oberen Fensterrand.
Alle Abstaende stehen jetzt im CSS-Block, schmal 14px oben und 10px
unten.
Dazu das mitgelieferte App-Symbol neu gezeichnet: Die drei PNG lagen
als Graustufen vor, der indigo Verlauf war zu Schwarz eingeebnet. Die
Wolke besteht jetzt aus drei Kreisen und einem Rechteck dazwischen -
gerade Unterkante, keine Kerben in der Silhouette.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Schickt der Anbieter ein `picture`, wird es bei der Anmeldung geholt,
mittig auf 256x256 zugeschnitten und oben rechts angezeigt. Fehlt es
oder scheitert das Holen, bleibt es beim Buchstabenkreis - die
Anmeldung selbst hängt nicht davon ab.
Die Bildadresse kommt aus einem Token, also von außen. Erlaubt ist
deshalb ausschließlich der Ursprung aus OIDC_ISSUER, Weiterleitungen
eingeschlossen; dazu 4 MB und 5 Sekunden als Deckel. Ohne das wäre es
eine Aufforderung an den Server, für einen Fremden beliebige Adressen
im internen Netz anzufragen.
Neu: Migration 000019 (avatar_key/-source/-updated_at), Paket
internal/avatar, Route GET /api/v1/users/{id}/avatar. Der GC durchsucht
"avatars/" mit und kennt die Schlüssel - sonst räumte er die Bilder
nach einem Tag weg.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Die Zahl stand doppelt im Quelltext: als const im Backend und noch einmal
im Verlaufs-Dialog, verbunden nur durch den Kommentar "muss zu
maxVersionsKept im Backend passen". Ein Satz ist keine Zusicherung - beim
ersten Aendern haette der Dialog eine andere Zahl genannt als die, die der
Server anwendet.
Jetzt eine .env-Variable (Vorgabe 5), die der Server ueber
/api/v1/public/config mitliefert. Werte unter 1 werden verworfen: Die Zahl
geht als OFFSET in die Aufraeumabfrage, und OFFSET 0 loeschte jede Fassung -
aus "keine Grenze" wuerde "kein Verlauf".
Gemessen: MAX_VERSIONS_KEPT=3, Datei siebenmal ueberschrieben, uebrig
bleiben v6, v5, v4 bei aktueller v7.
Dazu ein README-Abschnitt "Versionen" - Standard aus, warum das so ist,
Kontingent, und dass jedes Strg+S in Office eine Fassung anlegt. Bisher
stand zu alldem nichts im README.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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>