Files
tyx_AI_xhs/apps/web/src/dynamic-render-models.ts
T

197 lines
8.0 KiB
TypeScript

import type { CanvasState } from "@dada/shared-contracts";
import { fontOption, type FontOption } from "./text-assets.js";
import type { DynamicTemplateId } from "./dynamic-provider.js";
type CanvasElement = CanvasState["elements"][number];
export type DynamicTextValue =
| "city"
| "city_en"
| "day"
| "hour"
| "latitude_dms"
| "longitude_dms"
| "meridiem"
| "minute"
| "month"
| "month_en"
| "nickname"
| "time"
| "title"
| "year_short";
export interface DynamicImageLayer {
assetId: string;
height: number;
width: number;
x: number;
y: number;
}
export interface DynamicTextLayer {
align: "center" | "left";
color: string;
fontId: string;
fontSize: number;
value: DynamicTextValue;
x: number;
y: number;
}
export interface DynamicRenderModel {
halfSize: { height: number; width: number };
imageLayers: readonly DynamicImageLayer[];
sourceCandidateId: string;
textLayers: readonly DynamicTextLayer[];
}
export const DYNAMIC_RESOURCE_VERSION = "wp4-dynamic-source-v1";
const dynamicFont = (fontId: string): FontOption => ({
displayName: fontId,
fontId,
url: `/api/v1/assets/public/${DYNAMIC_RESOURCE_VERSION}/${fontId}`,
});
export const DYNAMIC_FONT_OPTIONS: readonly FontOption[] = [
dynamicFont("15974853bc3294ef68e7e6d58fe74fd7"),
dynamicFont("46f8336813e4c48d06a1aef294fdccf6"),
dynamicFont("53ca6b704728520da50c145eabb2e635"),
dynamicFont("cca5efc0e02fb1bf62349bd68ef30fc1"),
dynamicFont("dd25b35dcb7ba4476cbaa9a9592e39e2"),
dynamicFont("e4210c9872f0c279b35273f230809821"),
dynamicFont("f4bfd4132df2d6be97ceabadf3853505"),
] as const;
export const DYNAMIC_RENDER_MODELS: Readonly<Record<DynamicTemplateId, DynamicRenderModel>> = {
DYN001: {
halfSize: { height: 42, width: 130 },
imageLayers: [{ assetId: "DYN001-image28", height: 67, width: 219, x: -17.562, y: 2.203 }],
sourceCandidateId: "l_POI01",
textLayers: [{ align: "center", color: "#FFFFFF", fontId: "dd25b35dcb7ba4476cbaa9a9592e39e2", fontSize: 21, value: "title", x: 0, y: 0 }],
},
DYN002: {
halfSize: { height: 48, width: 215 },
imageLayers: [{ assetId: "DYN002-image29", height: 77, width: 232, x: 94.5, y: 0.5 }],
sourceCandidateId: "l_POI02",
textLayers: [
{ align: "left", color: "#FFFFFF", fontId: "15974853bc3294ef68e7e6d58fe74fd7", fontSize: 19, value: "title", x: -94.5, y: 10.5 },
{ align: "left", color: "#FFFFFF", fontId: "15974853bc3294ef68e7e6d58fe74fd7", fontSize: 10, value: "city_en", x: 0, y: 36 },
],
},
DYN003: {
halfSize: { height: 54, width: 215 },
imageLayers: [{ assetId: "DYN003-image30", height: 85, width: 235, x: -93.857, y: -23.635 }],
sourceCandidateId: "l_POI03",
textLayers: [
{ align: "left", color: "#FFFFFF", fontId: "dd25b35dcb7ba4476cbaa9a9592e39e2", fontSize: 18, value: "title", x: -54, y: 9 },
{ align: "left", color: "#FFFFFF", fontId: "dd25b35dcb7ba4476cbaa9a9592e39e2", fontSize: 24, value: "city", x: -53, y: -16 },
],
},
DYN004: {
halfSize: { height: 48, width: 180 },
imageLayers: [{ assetId: "DYN004-image32", height: 76, width: 39, x: -21.067, y: -1 }],
sourceCandidateId: "l_POI04",
textLayers: [
{ align: "left", color: "#FFFFFF", fontId: "dd25b35dcb7ba4476cbaa9a9592e39e2", fontSize: 26, value: "city", x: -28, y: 0 },
{ align: "left", color: "#FFFFFF", fontId: "15974853bc3294ef68e7e6d58fe74fd7", fontSize: 14, value: "latitude_dms", x: -29.844, y: 36 },
{ align: "left", color: "#FFFFFF", fontId: "15974853bc3294ef68e7e6d58fe74fd7", fontSize: 14, value: "longitude_dms", x: 71.523, y: 36 },
],
},
DYN007: {
halfSize: { height: 34, width: 190 },
imageLayers: [],
sourceCandidateId: "diaoyu",
textLayers: [{ align: "center", color: "#295E8B", fontId: "53ca6b704728520da50c145eabb2e635", fontSize: 50, value: "nickname", x: 0, y: 0 }],
},
DYN008: {
halfSize: { height: 180, width: 140 },
imageLayers: [{ assetId: "DYN008-backendui0", height: 185, width: 5, x: -125, y: 0 }],
sourceCandidateId: "l_shijian2",
textLayers: [
{ align: "left", color: "#FFFFFF", fontId: "f4bfd4132df2d6be97ceabadf3853505", fontSize: 70, value: "month_en", x: -112, y: -32 },
{ align: "left", color: "#FFFFFF", fontId: "f4bfd4132df2d6be97ceabadf3853505", fontSize: 130, value: "time", x: -114.024, y: 97 },
],
},
DYN011: {
halfSize: { height: 40, width: 130 },
imageLayers: [{ assetId: "DYN011-backendui0", height: 12, width: 8, x: -120, y: -19 }],
sourceCandidateId: "l_shijian6",
textLayers: [
{ align: "center", color: "#FF6D2F", fontId: "e4210c9872f0c279b35273f230809821", fontSize: 64, value: "year_short", x: -85, y: 0 },
{ align: "center", color: "#FF6D2F", fontId: "e4210c9872f0c279b35273f230809821", fontSize: 64, value: "month", x: 0, y: 0 },
{ align: "center", color: "#FF6D2F", fontId: "e4210c9872f0c279b35273f230809821", fontSize: 64, value: "day", x: 85, y: 0 },
],
},
DYN012: {
halfSize: { height: 78, width: 118 },
imageLayers: [],
sourceCandidateId: "l_shijian7",
textLayers: [],
},
DYN015: {
halfSize: { height: 42, width: 300 },
imageLayers: [{ assetId: "DYN015-imager2", height: 60, width: 60, x: -140, y: 0 }],
sourceCandidateId: "0721userna",
textLayers: [{ align: "left", color: "#666666", fontId: "cca5efc0e02fb1bf62349bd68ef30fc1", fontSize: 40, value: "nickname", x: -100, y: 0 }],
},
DYN016: {
halfSize: { height: 48, width: 160 },
imageLayers: [{ assetId: "DYN016-image21", height: 59, width: 186, x: -61.842, y: 16.483 }],
sourceCandidateId: "l_username00",
textLayers: [{ align: "left", color: "#FFFFFF", fontId: "46f8336813e4c48d06a1aef294fdccf6", fontSize: 27, value: "nickname", x: 0, y: 0 }],
},
};
export function dynamicFontOptionsFor(templateId: string) {
if (templateId === "DYN012") {
const replacement = fontOption("FONT081");
return replacement ? [replacement] : [];
}
const model = DYNAMIC_RENDER_MODELS[templateId as DynamicTemplateId];
if (!model) return [];
const ids = new Set(model.textLayers.map((layer) => layer.fontId));
return DYNAMIC_FONT_OPTIONS.filter((option) => ids.has(option.fontId));
}
export function dynamicImageUrl(resourceVersion: string, assetId: string) {
return `/api/v1/assets/public/${encodeURIComponent(resourceVersion)}/${encodeURIComponent(assetId)}`;
}
function numericField(element: CanvasElement, key: string) {
const value = element.dynamic_fields?.[key];
return typeof value === "number" ? value : Number(value);
}
function coordinate(value: number, positive: string, negative: string) {
if (!Number.isFinite(value)) return "";
const absolute = Math.abs(value);
const degrees = Math.floor(absolute);
const minutesFloat = (absolute - degrees) * 60;
const minutes = Math.floor(minutesFloat);
const seconds = (minutesFloat - minutes) * 60;
return `${degrees}°${String(minutes).padStart(2, "0")}'${seconds.toFixed(2).padStart(5, "0")}\"${value >= 0 ? positive : negative}`;
}
export function dynamicTextValue(element: CanvasElement, value: DynamicTextValue) {
const fields = element.dynamic_fields ?? {};
const text = (key: string) => String(fields[key] ?? "");
const override = fields.display_override;
if (typeof override === "string" && (value === "nickname" || value === "title")) return element.formatted_value ?? override;
if (value === "time") return `${text("hour")}:${text("minute")}`;
if (value === "month_en") {
const month = Math.max(1, Math.min(12, numericField(element, "month")));
return `${["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][month - 1]}.`;
}
if (value === "year_short") return text("year").slice(-2);
if (value === "latitude_dms") return coordinate(numericField(element, "latitude"), "N", "S");
if (value === "longitude_dms") return coordinate(numericField(element, "longitude"), "E", "W");
if (value === "meridiem") {
const hour = numericField(element, "hour");
const minute = numericField(element, "minute");
return hour > 12 || (hour === 12 && minute > 0) ? "PM" : "AM";
}
return text(value);
}