feat: enhance medialib image handling and add asset URL resolution

- Implemented `resolveApiAssetUrl` function to normalize asset URLs based on API base.
- Updated `MedialibImage` component to utilize new asset URL resolution and added support for alt text and class properties.
- Enhanced image loading behavior with improved width measurement and focal point handling.
- Added placeholder image handling and improved accessibility with alt text.
- Introduced new test script for auditing broken links in skill documentation.
- Expanded seeded test content to include medialib entries and updated related tests for pagebuilder previews.
- Improved global setup and teardown logging for clarity on seeded content management.
This commit is contained in:
2026-05-17 00:52:41 +00:00
parent 958b45272d
commit 4020ad62c5
44 changed files with 4276 additions and 867 deletions
@@ -343,6 +343,48 @@ decrementRequests() → LoadingBar disappears
Return { data, count, buildTime }
```
### `_count` endpoint
Der tibi-server stellt einen dedizierten `_count`-Endpoint bereit, der **nur** `{"count": N}` zurückgibt kein Data-Transfer:
```
GET /api/{collection}/_count?filter={"active":true,"category":"<id>"}
→ {"count": 8}
```
Der Endpoint wird durch den BrowserSync-Proxy korrekt geroutet (`/api``/api/v1/_/{namespace}`).
**Frontend-Aufruf:**
```ts
const res = await api<{ count: number }>("machines/_count", {
filter: { active: true, category: catId },
})
// res.data.count === 8
```
Das ist effizienter als `count=1&limit=1`, weil keine Collection-Objekte serialisiert/übertragen werden.
### `select` für schlanke Queries
Der tibi-server unterstützt einen `select`-Parameter als Komma-Liste der gewünschten Felder. Nicht gelistete Felder werden nicht übertragen:
```ts
const res = await api<MachineEntry[]>("machines", {
filter: { active: true, category: catId },
sort: "sortOrder",
limit: 20,
params: {
lookup: "images:medialib",
select: "name,slug,tagline,priceFrom,weight,sku,images",
},
})
```
Nicht aufgeführte Felder (z.B. `description`, `specs`) entfallen spart Bandbreite bei Listen/Grids. `_lookup` und `id` werden automatisch ergänzt.
**Wichtig:** `select` muss als String im `params`-Objekt übergeben werden (der `apiRequest` hängt es als Query-Parameter an). Es wird direkt an den tibi-server durchgereicht.
---
## i18n system
@@ -389,3 +431,30 @@ Return { data, count, buildTime }
- **Content cache is 1 hour** — `getCachedEntries` caches in memory for 1h. For admin previews, use `getDBEntries` (uncached).
- **`$effect` alone is not SSR** — server-side rendering must trigger the same data path explicitly outside browser-only reactive effects.
- **A rendered shell is not enough** — always verify that SSR HTML actually contains page-critical content and navigation.
## Cart persistence (localStorage)
For SSR-safe cart/inquiry persistence:
```ts
let cartItems = $state<any[]>([])
// Laden
$effect(() => {
try {
if (typeof localStorage !== "undefined") {
const saved = localStorage.getItem("cart_key")
if (saved) cartItems = JSON.parse(saved)
}
} catch {}
})
// Speichern
$effect(() => {
try {
if (typeof localStorage !== "undefined") {
localStorage.setItem("cart_key", JSON.stringify(cartItems))
}
} catch {}
})
```
Immer mit `typeof localStorage !== "undefined"` für SSR-Sicherheit.