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
+17 -10
View File
@@ -137,13 +137,15 @@ const CACHE_TTL = 1000 * 60 * 60 // 1 hour
// Generic collection helpers
// ---------------------------------------------------------------------------
type CollectionNameT = "medialib" | "content" | string
type CollectionNameT = "medialib" | "content" | "navigation" | string
type EntryTypeSwitch<T extends string> = T extends "medialib"
? MedialibEntry
: T extends "content"
? ContentEntry
: Record<string, unknown>
: T extends "navigation"
? NavigationEntry
: Record<string, unknown>
export async function getDBEntries<T extends CollectionNameT>(
collectionName: T,
@@ -152,7 +154,8 @@ export async function getDBEntries<T extends CollectionNameT>(
limit?: number,
offset?: number,
projection?: string,
params?: Record<string, string>
params?: Record<string, string>,
lookup?: string
): Promise<EntryTypeSwitch<T>[]> {
const c = await api<EntryTypeSwitch<T>[]>(collectionName, {
filter,
@@ -161,6 +164,7 @@ export async function getDBEntries<T extends CollectionNameT>(
offset,
projection,
params,
lookup,
})
return c.data
}
@@ -172,13 +176,14 @@ export async function getCachedEntries<T extends CollectionNameT>(
limit?: number,
offset?: number,
projection?: string,
params?: Record<string, string>
params?: Record<string, string>,
lookup?: string
): Promise<EntryTypeSwitch<T>[]> {
const filterStr = obj2str({ collectionName, filter, sort, limit, offset, projection, params })
const filterStr = obj2str({ collectionName, filter, sort, limit, offset, projection, params, lookup })
if (cache[filterStr] && cache[filterStr].expire >= Date.now()) {
return cache[filterStr].data as EntryTypeSwitch<T>[]
}
const entries = await getDBEntries<T>(collectionName, filter, sort, limit, offset, projection, params)
const entries = await getDBEntries<T>(collectionName, filter, sort, limit, offset, projection, params, lookup)
cache[filterStr] = { expire: Date.now() + CACHE_TTL, data: entries }
return entries
}
@@ -187,18 +192,20 @@ export async function getDBEntry<T extends CollectionNameT>(
collectionName: T,
filter: MongoFilter,
projection?: string,
params?: Record<string, string>
params?: Record<string, string>,
lookup?: string
): Promise<EntryTypeSwitch<T> | undefined> {
return (await getDBEntries<T>(collectionName, filter, "_id", 1, undefined, projection, params))?.[0]
return (await getDBEntries<T>(collectionName, filter, "_id", 1, undefined, projection, params, lookup))?.[0]
}
export async function getCachedEntry<T extends CollectionNameT>(
collectionName: T,
filter: MongoFilter,
projection?: string,
params?: Record<string, string>
params?: Record<string, string>,
lookup?: string
): Promise<EntryTypeSwitch<T> | undefined> {
return (await getCachedEntries<T>(collectionName, filter, "_id", 1, undefined, projection, params))?.[0]
return (await getCachedEntries<T>(collectionName, filter, "_id", 1, undefined, projection, params, lookup))?.[0]
}
export async function postDBEntry<T extends CollectionNameT>(
+29
View File
@@ -1,3 +1,32 @@
import { get } from "svelte/store"
import { apiBaseURL } from "../config"
import { apiBaseOverride } from "./store"
function isAbsoluteUrl(url: string | undefined): boolean {
return !!url && (/^(?:https?:)?\/\//.test(url) || url.startsWith("data:") || url.startsWith("blob:"))
}
function normalizeBase(base: string | null | undefined): string | null {
return base ? base.replace(/\/+$/, "") + "/" : null
}
export function resolveApiAssetUrl(
url: string | null | undefined,
apiBase: string | null | undefined = get(apiBaseOverride) || apiBaseURL
): string | null | undefined {
if (!url || isAbsoluteUrl(url) || !url.startsWith("/assets/")) {
return url
}
const normalizedApiBase = normalizeBase(apiBase)
if (!normalizedApiBase) {
return url
}
return normalizedApiBase + "_/assets/" + url.replace(/^\/+/, "")
}
export function debounce<T extends (...args: never[]) => void>(
func: T,
wait: number