Files
eventmap/docs/adr/0001-vanilla-esm.md
T
dapelzaandClaude Sonnet 5 2aaff8f680 Seed-Daten per Fetch von externem Server laden, UI-Sprachumschalter (DE/EN) und KI-Übersetzungs-Prompt-Generator ergänzen
- Beispieldaten werden nicht mehr eingebettet (data/seed.js entfernt),
  sondern beim ersten Start per fetch(EventMap.config.SEED_DATA_URL) von
  einem externen, selbst gehosteten Server geladen (siehe ADR 0001,
  Revision 2026-07-28).
- Neuer UI-Sprachumschalter (Deutsch/Englisch) für die Bedienoberfläche:
  js/i18n/strings.js + js/i18n/i18n.js, Sprachwahl im Menü, Persistenz über
  settings.language, Retranslation ohne Reload.
- Neuer Menüpunkt "Ausflugsziel übersetzen (KI)": erzeugt einen Prompt, der
  eine externe KI bittet, Name/Beschreibungen eines bestehenden Ortes ins
  Englische, Spanische oder Französische zu übersetzen (js/ai/translatePromptBuilder.js,
  js/ui/translatePromptGenerator.js).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-28 15:16:31 +02:00

113 lines
6.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR 0001: Kein Build-Tooling, klassische Scripts mit globalem Namespace, Leaflet lokal vendored
## Status
Akzeptiert (überarbeitet siehe "Revision" unten; ursprüngliche
Entscheidung für native ES-Module wurde revidiert)
## Kontext
`anforderung.md` fordert explizit "möglichst einfache und gut verständliche
Technologien" (HTML5, CSS3, JavaScript, eine Open-Source-Kartenbibliothek,
LocalStorage) und stellt keinen Backend-Zwang für die erste Version. Die
Anwendung soll außerdem vollständig responsiv sein und ohne größere
Infrastruktur betrieben und gewartet werden können.
Die tatsächliche Zielumgebung des Nutzers wurde präzisiert: Die App wird
per **Doppelklick auf `index.html`** direkt aus einem lokal synchronisierten
Nextcloud-Ordner geöffnet (`file://...`). Es steht **kein lokaler
HTTP-Server** zur Verfügung und ein solcher soll vom Endnutzer auch nicht
manuell gestartet werden müssen Priorität hat maximale Einfachheit für den
Endnutzer, nicht Entwicklerkomfort.
## Revision (2026-07-24)
Die ursprüngliche Entscheidung, JavaScript als native ES-Module
(`<script type="module">` mit `import`/`export`) umzusetzen, war für diesen
Anwendungsfall **falsch**: Browser blockieren das Nachladen von ES-Modulen
über `file://` per CORS-Policy (`Access to script ... has been blocked by
CORS policy`). Die App ließ sich damit nur über einen lokalen HTTP-Server
starten was der Anforderung "Doppelklick, kein Server" widerspricht.
**Neue Entscheidung:** Alle JavaScript-Dateien wurden von ES-Modulen auf
**klassische, nicht-modulare `<script>`-Tags** umgebaut. Statt
`import`/`export` hängt jede Datei ihre öffentliche API an ein einziges
globales Namespace-Objekt `window.EventMap` (mit Sub-Namespaces `utils`,
`config`, `models`, `store`, `map`, `ui`) an. Jede Datei kapselt ihre
internen Hilfsvariablen weiterhin in einer IIFE
(`(function () { ... })();`), sodass nur das explizit an `EventMap.*`
angehängte gemeinsam genutzt wird. Die Ladereihenfolge in `index.html`
(Utils → Config → Models → Store → Map → UI → `main.js`) stellt sicher,
dass jede Datei beim Ausführen bereits auf die Namespaces ihrer
Abhängigkeiten zugreifen kann.
## Entscheidung
- Es wird **kein Build-Tooling** (kein Bundler wie Webpack/Vite, kein
Transpiler, kein Package-Manager-Setup für die App selbst) eingesetzt.
- JavaScript wird als **klassische Scripts** (`<script src="...">`, ohne
`type="module"`) eingebunden, organisiert über einen gemeinsamen globalen
Namespace `window.EventMap` statt über `import`/`export`. Jede Datei ist
in eine IIFE gekapselt.
- **Leaflet** und **Leaflet.markercluster** werden **lokal vendored** unter
`vendor/leaflet/` bzw. `vendor/leaflet.markercluster/` (JS, CSS und
Marker-Bildassets), nicht per CDN eingebunden.
## Begründung
- Entspricht direkt der Anforderung nach einfachen, gut verständlichen
Technologien ohne zusätzliche Lernkurve (kein Framework, kein Build-Schritt).
- Ermöglicht das Öffnen der App per Doppelklick aus dem synchronisierten
Ordner heraus, ohne dass der Endnutzer einen Server starten oder
irgendetwas installieren muss das ist für die Zielgruppe wichtiger als
natives Modul-Tooling.
- Lokal vendorte Bibliotheken machen die App **offline-fähig** und
unabhängig von der Verfügbarkeit externer CDNs; das passt zum
Offline-First-Ansatz der LocalStorage-Datenhaltung.
- Geringere Angriffsfläche/Komplexität: keine Abhängigkeit von Node-Toolchain-
Versionen, keine Build-Artefakte, die aktuell gehalten werden müssten.
## Konsequenzen
- **Trade-off gegenüber ES-Modulen:** Es gibt kein natives Modul-Scoping
mehr. Stattdessen dient der globale `window.EventMap`-Namespace plus
IIFE-Kapselung pro Datei als Ersatz. Das erfordert Disziplin beim
Vergeben von Namen unter `EventMap.*` (keine versehentlichen
Überschreibungen) und macht Abhängigkeiten weniger explizit als
`import`-Statements sie ergeben sich aus der Script-Reihenfolge in
`index.html`, die entsprechend sorgfältig gepflegt werden muss.
- **Behobenes Restrisiko (2026-07-24):** Ursprünglich lud `js/store/store.js`
die Beispieldaten per `fetch('data/seed.json')`, was unter `file://`
(insbesondere in Chrome/Chromium) von der Same-Origin-Policy blockiert
wird. Die Seed-Daten wurden daher von `data/seed.json` nach
`data/seed.js` verschoben und lagen dort als eingebettetes
`<script>`-Objekt (`EventMap.data.SEED_PLACES`), das wie jede andere
JS-Datei klassisch eingebunden wurde. `initStore()` war dadurch
synchron. Für spätere Phasen (z.B. KI-JSON-Import) gilt weiterhin:
Datei-Uploads über `<input type="file">` + `FileReader` sind
unproblematisch unter `file://`, ein `fetch()` auf *lokale* Dateien
dagegen nicht.
- **Revision (2026-07-28):** `data/seed.js` wurde aus dem Repo entfernt.
Die Beispieldaten werden jetzt beim allerersten Start per
`fetch(EventMap.config.SEED_DATA_URL)` von einem externen, selbst
gehosteten Server geladen (siehe `js/store/store.js`,
`loadSeedPlaces()`/`initStore()`). Das ist kein Rückfall in das oben
beschriebene Problem, da sich die Same-Origin-Blockade unter `file://`
nur auf *lokale* Dateien bezieht ein `fetch()` auf eine entfernte
`https://`-URL funktioniert auch von `file://` aus, sofern der Server
passende CORS-Header (`Access-Control-Allow-Origin`) sendet.
`initStore()` ist dadurch wieder asynchron (`main.js` awaitet es
bereits). Schlägt der Download fehl (offline, fehlende CORS-Header,
Server nicht erreichbar), startet die App bewusst mit einer leeren
Places-Liste und zeigt einen Hinweis im Status-Banner statt die
gesamte Initialisierung abzubrechen.
- Es gibt keinen automatischen Minify-/Tree-Shaking-Schritt; die
Dateigröße im Produktivbetrieb ist etwas größer als mit Bundler, was für
den Umfang dieser Anwendung akzeptabel ist.
- Updates der vendorten Bibliotheken müssen manuell durchgeführt werden
(Dateien in `vendor/` austauschen), es gibt keinen automatisierten
Dependency-Update-Mechanismus (z.B. Dependabot) für diese Assets.
- Sollte die Anwendung später deutlich wachsen (z.B. TypeScript, größere
Testinfrastruktur, Bereitstellung über einen echten Webserver), kann
diese Entscheidung revidiert und zurück auf ES-Module umgestellt werden.