Commit Graph
13 Commits
Author SHA1 Message Date
kantarciandClaude Opus 5 e8a9dcdf5c Upload PDFs to the library, link them from any link target, show them in a viewer
PDFs go through the same upload field as images, are checked by their content and kept
unchanged in their own table. Every link field can pick one, and a page that links a PDF
opens it in a dialog with the browser's own viewer; touch devices and browsers without a
viewer get a new tab instead.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 07:56:29 +02:00
kantarciandClaude Opus 5 45d1141951 Show the own site icon to Google under every address, and the KANTARCI landing page
- Google kept showing the KantSites K for Elektro Bernhard after the site
  icon was changed. The own icon only existed at 32 and 180 px, and Google
  takes multiples of 48 only; /favicon.svg was a file in public/, so the web
  server always delivered the bundled K.
- The icon is now also cut at 48 and 192 px. The head links /favicon-192.png,
  /favicon.ico serves the 48 px PNG, and /favicon.svg is answered by the
  application: the bundled mark without an own icon (now in
  public/assets/favicon.svg), otherwise a 302 to the 192 px PNG. Sites that
  set their icon earlier get the new sizes on first request. A removed icon
  no longer counts even though its files remain.
- The archive update only adds files to public/, so it now deletes the old
  public/favicon.svg explicitly.
- Seventh local reference site: the KANTARCI Immobilien landing page with a
  three-step rental request, own database, seed and reset scripts.

Tests: 582 PHPUnit on PHP 8.2 and 8.4, 35 node, test:editor and
test:browser green (test:browser reports only the known editor colour
contrast on /grundlagen).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 07:37:04 +02:00
kantarciandClaude Opus 5 106f84dfd5 Pick the site icon from the library and crop it, find legal pages by search, list newest first
- The site icon is no longer a select at the end of the company data. It has
  its own section: a button opens the media library (upload included), then
  a square is dragged, resized by its corner, a slider or +/-, and previewed
  at 180 and 32 px. The crop is stored as fractions (faviconCrop) and the
  PNGs are cut from it; faviconVersion changes the link so browsers pick up
  a new cut. Icons without a crop keep their old link.
- openMedia() takes needsAlt: false, since the icon never appears as
  content. The dialog styles moved from editor.css to media-dialog.css so
  the dialog works outside the editor.
- Privacy page and imprint are chosen with the content search, like the 404
  page. The picker can store the path instead of the id (valueKey/data-value).
- The page and post lists in the administration sort newest first; the
  subpage indentation is gone there, as it would point at the wrong parent.

Tests: 581 PHPUnit, 35 node, test:editor green (the consent step now picks
the privacy page by search). test:admin --admin-only fails only at the known
nested-interactive menu summaries.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 07:43:05 +02:00
kantarciandClaude Fable 5.1 d0019c80af Arrow icons, cascading own CSS, a preload for the hero image, and EB Solar
Built while setting up the redesign of ebsolar.de as the sixth local
installation (scripts/seed-ebsolar.php, reset-ebsolar.sh, ports 8096/8097).
The rebuild surfaced four things in the core:

- The icon set knows three arrows now: arrow-right, arrow-left,
  arrow-up-right. The button block and the post navigation render them as
  inline SVG inside the same aria-hidden span as before, so a template that
  hides the arrow with "& span{display:none}" keeps working. Until now the
  arrow was a text glyph that looked different in every font. Umut: "bei
  alle Projekte bzw bei allen möchte ich dieses emoji icon weg haben".

- Own CSS with the same nested selector twice kept only the last rule.
  "& .post-card{border:0}" followed by "& .post-card{transform:none}" lost
  the border - silently, which brought the core's card frame back on the
  project grid. The parser now joins the declarations in source order, as
  CSS does; the same for "&{…}" next to the element's own properties.

- The eager image over the fold was not preloaded. The background image of
  the first section has had a preload link for a while; a plain <img> was
  found only after the stylesheet was parsed, and that round trip went
  straight into LCP. PageController::heroImage() now writes the same src,
  srcset and sizes the block template writes, and page.html.twig emits the
  link - unless a background image is already preloaded.

- Measured on the way: an entrance effect on the LCP element costs about
  900 ms of render delay. The EB Solar hero image therefore has none.

Tests: 581 green, the icon test accepts quoted keys, two cases for the CSS
cascade, the preload asserted in the big-picture test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-17 12:56:57 +02:00
kantarciandClaude Opus 5 c8a8fa7460 Rebuild DEEG as a fifth installation, add stepped forms and a plugin system, and clear the review backlog
Forms gained steps, conditions, image choices, number fields with units and a
summary step; the DEEG price finder uses all of it. Plugins are manifest plus
Twig: blocks, settings, containers, routes, presets and theme packs, switched
per installation. The review backlog is done: archive pages render through the
normal page pipeline, forms and post lists work inside areas and sections,
repeated sections get their own ids, breakpoints and colours come from the
palette, backup and update work on MySQL and lock each other out.

The task worker no longer shortens a process that has no time limit:
set_time_limit() sets the limit instead of extending it, which turned an
unlimited CLI run into eighty seconds and killed the full test suite far away
from the scheduler.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 07:32:35 +02:00
kantarciandClaude Fable 5.1 533e181590 Rebuild maler-drews.com as a fourth installation and fix two public-page gaps
Ihr Malerservice Drews (Gernsbach) as its own installation: seed script with
theme, header, footer, eight pages, 31 posts in two categories, contact form,
consent-gated map and videos, redirects; the wording lives in a generated
content file. Reset script, launch entry on port 8093, Cormorant Garamond
served locally with its OFL text.

Two CMS fixes found while building: wide and full sections lost against the
theme's .kant .shell max-width, and the scroll-top link sat outside every
landmark (axe "region"). Lighthouse on six pages: accessibility, best
practices, SEO and agentic browsing 100, performance 98-100; axe zero
violations on nine pages at 1440 and 390 px.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 11:11:42 +02:00
kantarciandClaude Fable 5.1 d05dba3042 Let a form send from its own address in 1.15.1
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s
Umut asked where a contact form's mail is sent from: it was the recipient
address, `From` equal to `To`. That breaks as soon as the recipient is a
mailbox at another provider, because most hosts only deliver mail that
claims an address of their own domain, and the provider's SPF/DMARC then
treats it as forged.

Each form now has an optional `sender` setting, validated like the
recipient; empty keeps the old behaviour, so stored forms need no
migration. Delivery uses `sender ?: recipient`; the reply-to field is
untouched. The administration shows the field under the recipient with a
note on why it matters. The pilot launch config moves to port 8091 because
8087 was held by another session.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 10:16:15 +02:00
kantarciandClaude Fable 5.1 160c394c3f Rework forms, statistics, design and the sidebar foot in 1.15.0
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s
Forms are an editor now: one card per field with add and remove, the
technical name derived from the label, options only for a choice, and a
new "Aussehen" section with a button label and four colours (field border
and background, button and button text) that travel to the page as one
rule per form inside the hashed style block. A live preview draws the
form with the site's default button and names the text contrast.

Statistics open with six figures (views against the same span before,
average per day, busiest day, pages, referred views, missing addresses),
a chart with grid lines and a title per bar, a weekday chart and ranked
lists with share bars. The design screen shows the palette as colour
cards with seven ready-made palettes, picks the base colours from the
palette, previews every font in itself and the typography as a sample.
The sidebar ends in an account card with role and a logout icon.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-15 07:59:30 +02:00
kantarciandClaude Opus 5 a434e0cefc Serve every font from this server and surface open tasks in 1.14.0
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s
Fonts were still reachable from Google in two places. The editor and area
previews were handed `googleFonts` and no `localFonts`, so the screen used
most of the day fetched Lato from fonts.googleapis.com on every open; the
public page fell back to the same link whenever the files were missing.
Neither path remains: the template has no Google branch, `googleFontsUrl()`
is gone, and the policies name no foreign font host - `font-src 'self'`
publicly, `style-src 'self'; font-src 'self'` in administration. Without
local files a site now shows the theme's fallback and says so.

Preloading took the first two weights per family. The Kant-IT theme names
Lato 400, 700 and 900 and sets its headings on 800/900, so the one weight
that carried them loaded after the stylesheet and `font-display:swap`
swapped the heading in plain sight. Every weight of the theme is preloaded
now, latin subset, and the links sit before the stylesheet: measured cold,
all three files finish at 168 ms against a first paint at 292 ms.

Open tasks no longer hide on the screen that owns them. A strip above every
management screen names what needs a button and carries it: a new version
leads to System, missing fonts to Design. Neither asks the network - the
version comes from the daily update-check task, the fonts from a look in
the folder.

System and update was unusable on a phone: `.stack` is a grid whose
automatic column grows to its widest content, and one long database path
pushed the page sideways. It gets `minmax(0, 1fr)`, the backup table stacks
below 620px, and the prose is 45% shorter with the fine print folded away.

The device chips drew the current device with an outline, which lies outside
the box, ran under its neighbour and was clipped by the sidebar - it read as
a cut-off red frame. Used devices are filled in `#9d4e0d` now (5.6:1), the
current one carries a border inside the chip.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 00:16:16 +02:00
kantarciandClaude Fable 5.1 fd4cb75548 Add social profiles as a block and rebuild Elektro Bernhard in 1.12.0
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s
Social profiles are maintained once under settings (one HTTPS address per
network, SocialProfiles::NETWORKS) and shown by the new `social` block
(document schema v23) wherever it stands: size, shape, layout, labels,
new tab and an optional subset of networks on the block; colour, hover,
spacing and alignment through the free block styles. The addresses also
feed sameAs in the structured data. Icons are own outline drawings in
the style of the existing icon set. ADR 093.

Elektro Bernhard is rebuilt with a dark hero over the team photo, a
transparent header that turns white on scroll, Lato, cards with shadows
and hover lift, a review slider with six real Google reviews, a dark
solar band, page headers on every subpage and staggered entrance
effects. Social profiles sit in the top bar and the footer. The site is
a second local reference installation next to the Kant-IT pilot
(var/elektro-bernhard.db, launch config on port 8088); the seed script
rebuilds it from scratch.

Checked: PHP suite 492 tests (only the documented old failure remains),
33 JS tests, Twig lint, PHP style, full editor browser check with axe.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-14 19:29:08 +02:00
kantarciandClaude Opus 5 84d471acd9 Das System heißt KantSites
Umut hat den Namen festgelegt: aus dem Arbeitstitel "Kant CMS" wird KantSites.
Geändert in allen Seitentiteln der Verwaltung, im Wortmarken-Schriftzug von
Anmeldung und Seitenleiste, in den Fußzeilen, in den Demoinhalten für eine
frische Installation, in package.json, .claude/launch.json und der
Dokumentation. Die Bezeichnung der Website in der Datenbank heißt jetzt
ebenfalls KantSites.

Nicht angefasst: die bereits gespeicherten Demoinhalte der lokalen
Installation. Titel wie "Die Grundlage · Kant CMS" und der Text im
Kopfbereich sind Inhalt, nicht Systemname - die gehören in den Editor, nicht
in ein Skript, das Revisionen umschreibt.

219 PHP-Tests / 881 Assertions grün, Playwright-Editortest grün.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 18:36:50 +02:00
kantarciandClaude Opus 5 33e4ac0c54 Frei wählbare Startseite; Standarddateien waren lokal unerreichbar
Eine Quelle entscheidet jetzt, welche Seite unter / antwortet:
SiteSettings::homePath() und publicPath(). Das Literal home kommt im
Anwendungscode nicht mehr vor - öffentliche Routen, Canonical,
Sitemap, llms.txt, Menüauflösung, Beitragslisten und der
Formularrücksprung benutzen dieselben zwei Methoden. Die gewählte
Seite muss veröffentlicht und nicht im Papierkorb sein, ihre eigene
Adresse leitet dauerhaft auf /, und als Wurzel ist sie gegen
Umbenennen und Papierkorb gesperrt.

Zweiter echter Fehler, den nur ein HTTP-Aufruf zeigt:
/sitemap.xml, /robots.txt, /llms.txt und /feed.xml waren lokal nie
erreichbar. Der eingebaute PHP-Server antwortet für fehlende Pfade
mit Dateiendung selbst mit 404, ohne den Frontcontroller zu fragen;
alle Kernel-Tests bestanden trotzdem, weil sie die Anwendung direkt
aufrufen. bin/dev-router.php bildet die Rewrite-Regel des Hostings
nach, Start- und CI-Befehl nutzen es, und die Browserprüfung ruft die
vier Adressen jetzt über HTTP auf.

Barrierefreiheitsbefunde werden nach Ursache getrennt: ein
Kontrastfehler aus Theme oder Stylesheet bleibt ein Defekt und bricht
den Lauf ab, einer aus einer im Editor gesetzten Blockfarbe wird bei
jedem Lauf mit Seite, Farbe und Verhältnis genannt und in
reports/browser.json geschrieben, färbt aber keinen Codewechsel rot.

Geprüft: 162 PHP-Tests / 636 Assertions unter PHP 8.2.12 und 8.4.22,
15 JavaScript-Modelltests, vollständiger Editortest mit axe, wieder
grüne öffentliche Browserprüfung und Lighthouse mit je drei Läufen
mobil und Desktop, alle fünf Kategorien 100.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 14:59:55 +02:00
kantarciandClaude Opus 5 a40c2bb27e SEO, Weiterleitungen und strukturierte Daten (Arbeitspaket E)
Migration 20260912000800 ergänzt page.robots, page.og_media_id,
website.settings und die Tabelle redirect.

Alles Maschinenlesbare wird aus dem veröffentlichten Bestand
abgeleitet, nichts separat gepflegt: die Sitemap listet nur
veröffentlichte, indexierbare Seiten samt Beitragsübersicht und
Begriffsarchiven, und dieselbe Einstellung setzt auch das
noindex-Meta - beide Aussagen können nicht auseinanderlaufen.

Strukturierte Daten entstehen nur aus gepflegten Werten: ohne
Angaben wird kein leerer Organization-Block ausgegeben. JSON-LD ist
ein Datenblock, kein Skript, daher bleibt script-src 'none'
unberührt.

Weiterleitungen greifen vor dem 404, aber nach der Seitensuche: eine
vorhandene Seite gewinnt immer, sonst könnte ein alter Eintrag eine
neue Seite unerreichbar machen. Ketten werden abgewiesen. robots.txt
nimmt zusätzliche Zeilen nur nach festem Muster an, damit ein
Eintrag die Datei nicht umdeuten kann. llms.txt ist standardmäßig
aus.

Geprüft: 128 PHP-Tests / 507 Assertions unter PHP 8.2.12 und 8.4.22,
14 JavaScript-Modelltests, Lint/Format/Container. Dazu eine reine
Sichtprüfung im Browser auf einer lokalen Demoseite - weiterhin
keine axe- oder Lighthouse-Messung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 12:43:00 +02:00