From d51c8445bb5d1fea409193215e768617859345cf Mon Sep 17 00:00:00 2001 From: Z8MB1E Date: Tue, 22 Sep 2026 21:09:02 -0400 Subject: [PATCH] feat(map): tile-mode basemap mosaic previews on map picker cards --- src/app/(frontend)/map/page.tsx | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/app/(frontend)/map/page.tsx b/src/app/(frontend)/map/page.tsx index 30176f1..18387a0 100644 --- a/src/app/(frontend)/map/page.tsx +++ b/src/app/(frontend)/map/page.tsx @@ -23,6 +23,21 @@ function basemapUrl(map: MapDoc): string | null { return null; } +/** + * Static preview for tile-mode maps: a 2x2 mosaic of the map's own zoom-1 + * tiles covering the whole world square, center-cropped by the card frame. + */ +function tilePreviewUrls(template: string): string[] | null { + if (!template.includes("{z}") || !template.includes("{x}") || !template.includes("{y}")) { + return null; + } + return [0, 1].flatMap((y) => + [0, 1].map((x) => + template.replace("{z}", "1").replace("{x}", String(x)).replace("{y}", String(y)), + ), + ); +} + export default async function MapPickerPage() { const payload = await getPayload({ config: await config }); @@ -70,6 +85,10 @@ export default async function MapPickerPage() { ? (map.factionControl as Faction) : null; const url = basemapUrl(map); + const tileUrls = + map.basemapMode === "tiles" && map.tileUrlTemplate + ? tilePreviewUrls(map.tileUrlTemplate) + : null; const stats = [ map.weather ? { @@ -101,6 +120,18 @@ export default async function MapPickerPage() { alt={map.name} className="size-full object-cover opacity-80 transition-opacity group-hover:opacity-100" /> + ) : tileUrls ? ( + ) : (