FileBrowser 1177 -> 712 Zeilen, PdfViewer 1193 -> 848. Herausgeloest wurde, was fuer sich zusammenhaengt: hooks/useUploads Warteschlange, Ordnerbaeume, Ablage components/files/ Werkzeugleiste, Dialoge, Menues, Reihenfolge components/pdf/ Werkzeugleiste, Zoomstufen, Seitenmasse, Suche src/format.ts formatBytes -- lag in einer *Seite* Dabei gefundene und behobene Fehler: * Ordner-Upload haengte Fortschritt und Fehler an die falsche Zeile, sobald in einem Ordner Dateien *nach* einem Unterordner lagen: Die Zeilen standen in Fundreihenfolge, hochgeladen wurde nach Ordnern gruppiert, und ein mitlaufender Zaehler verband beides falsch. * formatBytes gab es zweimal, zeichengleich (Dateibrowser + Admin). * Beide Sortierungen liefen bei *jedem* Bild -- der Rahmen beim Ziehen ist eines je Mausbewegung. Jetzt gemerkt. * Die Suche im PDF las bei jedem Tastendruck den Text aller Seiten neu. Jetzt 160 ms Wartezeit; "Searching..." bleibt auch bei einem Fehler nicht mehr stehen. * Seitenmasse werden in Buendeln statt streng nacheinander geholt. Pruefinfrastruktur: vitest stand auf 'jsdom', ohne dass jsdom je installiert war -- "npm test" brach ab, bevor es etwas ausfuehrte. Jetzt 'node' und 32 Tests fuer die herausgeloesten reinen Funktionen. Das Abbild baut nur noch mit gruenen Tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
152 lines
6.5 KiB
TypeScript
152 lines
6.5 KiB
TypeScript
import { cpSync, mkdirSync } from 'node:fs'
|
||
import { createRequire } from 'node:module'
|
||
import { dirname, join } from 'node:path'
|
||
import { defineConfig, type Plugin } from 'vite'
|
||
import react from '@vitejs/plugin-react'
|
||
import tailwindcss from '@tailwindcss/vite'
|
||
import { VitePWA } from 'vite-plugin-pwa'
|
||
|
||
/**
|
||
* Die 14 Standardschriften von PDF.js nach public/ kopieren.
|
||
*
|
||
* Aeltere PDFs betten Helvetica, Times und Courier nicht ein, sondern setzen
|
||
* sie voraus. Ohne diese Dateien malt PDF.js mit einer Ersatzschrift – lesbar,
|
||
* aber Zeilen brechen an anderer Stelle als im Original.
|
||
*
|
||
* Kopiert statt eingecheckt: 800 KB fremde Binaerdateien gehoeren nicht in den
|
||
* Quelltext, und die Fassung soll der Abhaengigkeit folgen. public/pdfjs steht
|
||
* deshalb in .gitignore.
|
||
*/
|
||
function pdfjsStandardFonts(): Plugin {
|
||
return {
|
||
name: 'aether-pdfjs-standard-fonts',
|
||
// Nicht 'buildStart': Im Entwicklungsbetrieb liest der Server public/
|
||
// beim Start ein, das Kopieren muss vorher passiert sein.
|
||
config() {
|
||
const require = createRequire(import.meta.url)
|
||
const from = join(dirname(require.resolve('pdfjs-dist/package.json')), 'standard_fonts')
|
||
const to = join(import.meta.dirname, 'public', 'pdfjs', 'standard_fonts')
|
||
mkdirSync(to, { recursive: true })
|
||
cpSync(from, to, { recursive: true })
|
||
},
|
||
}
|
||
}
|
||
|
||
export default defineConfig({
|
||
plugins: [
|
||
pdfjsStandardFonts(),
|
||
react(),
|
||
tailwindcss(),
|
||
VitePWA({
|
||
registerType: 'autoUpdate',
|
||
workbox: {
|
||
// Der Service Worker beantwortet Navigationen — also den Aufruf der
|
||
// Seite selbst — **Netz zuerst, Cache als Rueckfall**.
|
||
//
|
||
// Frueher lag index.html im Vorab-Cache und wurde von dort geliefert.
|
||
// Das war schnell, hatte aber eine unscheinbare Folge: Ein Eintrag im
|
||
// Cache ist eine ganze Antwort, **mitsamt ihren Kopfzeilen**. Der
|
||
// Browser bekam also dauerhaft die CSP von damals. Eine Aenderung an
|
||
// HONEYBAR_ORIGIN erreichte ihn nie — der Inhalt der Datei aendert sich
|
||
// ja nicht, und der Vorab-Cache holt nur nach, was sich geaendert hat.
|
||
// Sichtbar wurde das als Widerspruch, an dem nichts falsch aussah: Das
|
||
// Skript laedt den neuen Ursprung, die CSP erlaubt den alten.
|
||
//
|
||
// precacheFallback haelt dabei, was navigateFallback vorher leistete:
|
||
// Ist weder Netz noch Cache da, antwortet die vorab abgelegte
|
||
// index.html — Adressen wie /admin oder /s/<token> funktionieren also
|
||
// weiterhin offline.
|
||
//
|
||
// Ausgenommen sind Downloads und WebDAV: Ein Klick auf „Download"
|
||
// oeffnet eine Navigation zu /api/v1/files/<id>/download. Faengt der
|
||
// Worker die ab, liefert er die Anwendung statt der Datei — und es
|
||
// passiert sichtbar nichts. Sie sollen am Worker vorbeigehen, nicht
|
||
// von ihm beantwortet werden.
|
||
// Der Vorab-Cache beantwortet sonst auch den Aufruf von „/" — er
|
||
// rechnet index.html als Verzeichnisseite an. Genau daran scheiterte
|
||
// der erste Anlauf: Die Regel unten kam nie zum Zug, weil die Seite
|
||
// schon vorher aus dem Vorab-Cache beantwortet wurde, mitsamt alter
|
||
// CSP. Als reiner Rueckfall (precacheFallback) bleibt index.html dort
|
||
// liegen, nur eben nicht mehr als Antwort auf jede Navigation.
|
||
directoryIndex: null,
|
||
// vite-plugin-pwa setzt sonst von sich aus navigateFallback: 'index.html'
|
||
// — also genau die Vorab-Cache-Antwort, die wir hier los sein wollen.
|
||
// Die Sperrliste nimmt jede Adresse davon aus; zustaendig ist dann die
|
||
// Regel darunter.
|
||
navigateFallbackDenylist: [/./],
|
||
runtimeCaching: [
|
||
{
|
||
urlPattern: ({ request, url }) =>
|
||
request.mode === 'navigate' &&
|
||
!/^\/(api|dav|share|branding|wopi)\//.test(url.pathname),
|
||
handler: 'NetworkFirst',
|
||
options: {
|
||
cacheName: 'aether-seiten',
|
||
// Ohne Grenze haengt der Start am langsamsten Netz.
|
||
networkTimeoutSeconds: 3,
|
||
precacheFallback: { fallbackURL: 'index.html' },
|
||
},
|
||
},
|
||
],
|
||
// PDF.js bleibt aussen vor: Betrachter (435 KB), Arbeiter (1,3 MB)
|
||
// und Standardschriften (800 KB) sind zusammen zweieinhalb Megabyte,
|
||
// die nur braucht, wer ein PDF oeffnet. Vorab in den Cache zu legen
|
||
// hiesse, das jeder Installation aufzuladen.
|
||
//
|
||
// Offline waere davon ohnehin nichts gewonnen: Die Dokumente selbst
|
||
// liegen auf dem Server und werden nicht mitgecacht – ein Betrachter
|
||
// ohne Datei nuetzt nichts.
|
||
globIgnores: [
|
||
'**/pdfjs/standard_fonts/**',
|
||
'**/pdf.worker*.mjs',
|
||
'**/PdfViewer-*.js',
|
||
'**/PdfViewer-*.css',
|
||
],
|
||
maximumFileSizeToCacheInBytes: 3 * 1024 * 1024,
|
||
},
|
||
// Das Manifest liefert der Server (/manifest.webmanifest): Name, Farbe
|
||
// und Symbol stehen in der Datenbank und sollen dem Admin-Bereich
|
||
// folgen. Gebaut waeren sie fest verdrahtet, und ein hochgeladenes
|
||
// Symbol erreichte die installierte App nie.
|
||
manifest: false,
|
||
}),
|
||
],
|
||
server: {
|
||
host: true, // auf allen Interfaces lauschen (IPv4 + IPv6), nötig für VS-Code-Port-Forwarding
|
||
proxy: {
|
||
'/api': {
|
||
target: 'http://localhost:8080',
|
||
changeOrigin: true,
|
||
proxyTimeout: 60 * 60 * 1000,
|
||
timeout: 60 * 60 * 1000,
|
||
},
|
||
'/share': {
|
||
target: 'http://localhost:8080',
|
||
changeOrigin: true,
|
||
},
|
||
// Logo, App-Symbol und Manifest kommen ebenfalls vom Backend – ohne
|
||
// diese beiden Eintraege fehlen sie im Entwicklungsbetrieb.
|
||
'/branding': {
|
||
target: 'http://localhost:8080',
|
||
changeOrigin: true,
|
||
},
|
||
'/manifest.webmanifest': {
|
||
target: 'http://localhost:8080',
|
||
changeOrigin: true,
|
||
},
|
||
},
|
||
},
|
||
test: {
|
||
// **node, nicht jsdom.** Die Einstellung stand auf 'jsdom', ohne dass
|
||
// jsdom je installiert war – `npm test` brach deshalb ab, bevor es
|
||
// irgendetwas ausführte. Geprüft wird hier, was sich ohne Browser prüfen
|
||
// lässt: Reihenfolgen, Zoomstufen, Suche im Text, Einheiten.
|
||
//
|
||
// Wer eine Komponente prüfen will, schreibt `// @vitest-environment jsdom`
|
||
// an den Anfang seiner Datei und nimmt jsdom in die devDependencies auf.
|
||
environment: 'node',
|
||
globals: true,
|
||
setupFiles: './src/test-setup.ts',
|
||
},
|
||
})
|