Commit Graph

13 Commits

Author SHA1 Message Date
Peter Meier 8e43169b23 feat(windkarte): Stellungnahme-Kriterien per Windvorranggebiet
Deploy / verify (push) Successful in 56s
Deploy / deploy (push) Successful in 1m38s
Jedes Windvorranggebiet kann jetzt 1–n text_fragment-Einträge als
Stellungnahme-Kriterien referenzieren. Das Panel lädt diese lazy beim
Öffnen und zeigt sie als aufklappbare Accordion-Sektion.

Außerdem: alle hardcodierten Strings im Karten-Block (Panel, Map,
Block) durch t()-Aufrufe ersetzt; 7 neue Translation-Keys ergänzt.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 14:22:35 +02:00
Peter Meier a3f545d727 feat(windkarte): add per-area geo export + dynamic favicon
Deploy / verify (push) Successful in 1m1s
Deploy / deploy (push) Successful in 1m8s
- /api/vorranggebiete.geojson + .kml endpoints (filter via ?id=W-XX)
- Download-Buttons in WindAreaPanel pro selektiertes Gebiet
- Hover-Tooltip auf Polygon zeigt Fläche, geplante Anlagen, Höhe, Gemeinden
- Favicon + apple-touch-icon dynamisch aus data.logoUrl (CMS-Logo)
- alte windrad-PNGs in static/favicons/ entfernt
- WindkarteBlock list-fetch: _limit statt limit (RustyCMS-Param)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 12:14:58 +02:00
Peter Meier 5b4d59175b Redesign SearchableTextBlock UI — stronger visual hierarchy
Deploy / verify (push) Successful in 56s
Deploy / deploy (push) Successful in 1m6s
Green gradient header, white card with shadow, left-border accent on open
accordion items, hover-visible copy button in summary row, lighter dividers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:18:07 +02:00
Peter Meier 0b6174eb53 chore: update iconify subset (map-outline swap)
Deploy / verify (push) Successful in 51s
Deploy / deploy (push) Successful in 59s
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 15:31:42 +02:00
Peter Meier 39d7d70c8b feat(windkarte): Leaflet-Block für Windvorranggebiete SWT
Deploy / verify (push) Successful in 58s
Deploy / deploy (push) Successful in 1m18s
Neuer CMS-Block `wind_map` — platzierbar auf jeder Seite per Row-Content.
Fetcht wind_area-Einträge + GeoJSON-Asset client-side, rendert Leaflet-Karte
(CartoDB Light) mit farbcodierten Polygonen nach Planungsstatus. Klick auf
Gebiet öffnet Slide-in-Panel mit Kenndaten, Gemeinden, Stellungnahme-Link
und Puffer-Ringen (200/600/1000 m via Turf). Erweiterbar für 2. Entwurf 2026.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 09:20:32 +02:00
Peter Meier 23faef550e feat(calendar): image/attachment per event, direct links, copy link, ImageModal component
Deploy / verify (push) Successful in 58s
Deploy / deploy (push) Successful in 53s
- calendar_item schema: flyer → image (type: image), attachment (assetUrl)
- CalendarItemData: image + attachment fields
- event anchor IDs (#event-{slug}) with onMount hash scroll + auto-open
- share button uses direct calendar anchor URL
- copy link button (mdi:link-variant) copies anchor URL to clipboard
- ImageModal extracted as reusable component
- action buttons always show text (no expand-on-hover)
- onDestroy window guard (SSR fix)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 20:02:30 +02:00
Peter Meier 6fb2592eae feat(calendar): add feed subscription, bulk export, share & Google Cal
Deploy / verify (push) Successful in 1m0s
Deploy / deploy (push) Successful in 1m3s
- /api/kalender.ics SSR route: ICS feed of all calendar items (webcal-subscribable, Cache 1h)
- buildICSFeed() / downloadICSFeed() for multi-event ICS export
- googleCalUrl() deep-link pre-fills Google Calendar create form
- Web Share API per event (navigator.share on mobile, clipboard fallback on desktop)
  — "Kopiert\!" toast per event for 2s after clipboard write
- Actions bar (subscribe + bulk export) only rendered after onMount
  — Apple platforms: webcal:// link opens Calendar.app natively
  — All others: direct ICS download via <a download>

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 16:50:56 +02:00
Peter Meier 8db68e073c refactor(calendar): stack layout, wider grid, fixed cell height
Deploy / verify (push) Successful in 53s
Deploy / deploy (push) Successful in 57s
- Calendar widget stays stacked (calendar above events list)
- Grid widens to max-w-2xl with fixed h-10 cells (no aspect-square height growth)
- Gap between cells: 0.5 → 1.5
- Button system extracted to shared btn-* utility classes in app.css
- Replaced Google Calendar link + Google Maps with OSM
- Removed google icon from iconify subset

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 09:56:40 +02:00
Peter Meier 892afc2d9f refactor(post-events): read event data from resolved calendarItem
Deploy / verify (push) Successful in 47s
Deploy / deploy (push) Successful in 53s
Replace direct post.isEvent/eventDate/eventLocation reads with
getPostEventInfo() helper that derives info from the resolved
calendarItem reference. Loaders now resolve "calendarItem" and request
that field instead of legacy flat properties.

Also regenerate iconify mdi subset (map-marker-outline, download,
google, calendar-search, refresh).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 16:08:16 +02:00
Peter Meier b633ed2d2b fix(rusty-image): preserve animated GIFs through markdown image processing
Deploy / verify (push) Successful in 51s
Deploy / deploy (push) Successful in 54s
RustyCMS /api/transform decodes only the first frame of animated formats,
which silently kills GIF animation when markdown HTML images get rewritten
to /api/transform URLs. Skip the transform for `.gif` URLs so the original
asset URL flows through and animation survives.

Also picks up an arrow-right entry in the iconify mdi subset generated
during a UI build.
2026-05-07 13:06:37 +02:00
Peter Meier b7093b703a style(design): consistency sweep + UX upgrades
Deploy / verify (push) Successful in 49s
Deploy / deploy (push) Successful in 1m0s
- Palette: gray/zinc/slate/neutral → stein, red → fire, amber → erde,
  sky → himmel, green → wald
- Footer: replaced raw rgb with stein tokens
- Radius: unified to rounded-xs across cards, buttons, inputs, pagination
- Buttons: .btn-primary/.btn-secondary now font-medium; added secondary
- Card hover: -translate-y-0.5 + shadow + wald-300 border
- Header active link: wald border-bottom + wald-700 text
- Mobile nav: bigger touch targets + wald active accent
- Pagination: prev/next text labels + wald-500 active state with
  separated shape/color classes to avoid Tailwind conflicts
- Lead paragraph: scoped .markdown > p:first-child for top-level
  MarkdownBlock only via [data-block-type="markdown"]
- Section dividers: subtle wald-200 gradient between content rows
- Global focus-visible ring (wald-500)
- Inline hex → palette tokens (Badge amber, Tag custom-color contrast)
- Font weights snapped to design system (300/400/500/600/700)
- transition-all → transition-colors where only color changes
- Removed em-dashes from user-visible templates

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 01:06:22 +02:00
Peter Meier 96cd9e9c57 fix(strommix): SMARD as primary source — accuracy parity with audit
Deploy / verify (push) Successful in 45s
Deploy / deploy (push) Successful in 49s
Audit at 22:15 MESZ flagged the conventional sum (lignite + hard coal
+ gas + nuclear) as 19.1 GW in the widget vs 15.65 GW in reality. The
breakdown showed the gap was Energy-Charts' `Fossil gas` (6.9 GW)
which bundles natural gas + GuD + similar, while SMARD's `Erdgas`
filter 4069 (3.8 GW) tracks just natural gas the way the widget label
implies. SMARD is the canonical German grid feed; switching the
primary source resolves the discrepancy directly.

- New `lib/smard.ts` fetches the 11 relevant filter IDs in parallel:
  conventional (1223/4070/4069/1224), wind (4067/1225), solar (1226),
  hydro (1228), biomass (4066), other-conventional + pumped storage
  (1227/4071, surfaced separately), load (410). Two-step protocol
  (index.json → archive.json) handled inside; output is MW (MWh × 4)
  aligned to one common slot timestamp so every value on screen comes
  from the same point in time.
- Aggregator route uses SMARD for production + load, Energy-Charts
  only for cross-border saldo + day-ahead prices (where SMARD has no
  convenient single-call endpoint). `dataSource: "smard" |
  "energy-charts"` flag travels in the response so the widget can
  attribute the numbers and fall back gracefully when SMARD is down.
- StrommixBlock gets a "Werte prüfen" disclosure that lists every
  component (wind onshore + offshore, solar, the four conventional
  buckets, biomass, hydro, load) with its MW value and the slot
  timestamp + data source. Fact-check users can verify the addition
  themselves now without asking.

Verified live: SMARD @ 21:00 UTC = lignite 8484 + hard coal 3480 +
gas 3811 + nuclear 0 = 15.78 GW conventional. Matches the user's
ground-truth 15.65 GW within sampling noise.
2026-05-04 23:47:52 +02:00
Peter Meier d0dede92fe perf(iconify): tree-shake MDI to used icons (-99.5% JSON, -71% client bundle)
The previous setup loaded the full @iconify-json/mdi collection (7000+
icons, 2.95 MB) just to render the ~30 icons we actually use, producing a
2.99 MB client chunk.

generate-icons.mjs scans src/ for static `icon="mdi:..."` references,
extracts a minimal IconifyJSON containing only those icons (plus an
EXTRA_ICONS list for dynamic CMS-driven names) from
@iconify-json/mdi/icons.json, and writes it to a generated subset file.
iconify-offline.ts now registers that subset instead of the full pack.

  build/client: 5.6 MB -> 1.6 MB (-71%)
  build/server: 5.3 MB -> 2.4 MB (-55%)
  largest client chunk: 2.99 MB -> 127 KB (-96%)
  iconify subset: 2.95 MB -> 15.5 KB (-99.5%)

Hooked into predev/prebuild so the subset stays fresh; missing icons
fall back to the iconify API at runtime.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 16:08:47 +02:00