import { z } from 'zod' const colorHex = z.string().regex(/^#([0-9a-fA-F]{3}){1,2}$/) const baseObject = z.object({ id: z.string().min(1), x: z.number(), y: z.number(), width: z.number().positive(), height: z.number().positive(), rotate: z.number(), zIndex: z.number().int().nonnegative(), }) const textObject = baseObject.extend({ type: z.literal('text'), props: z.object({ text: z.string(), fontSize: z.number().positive(), color: colorHex, }), }) const imageObject = baseObject.extend({ type: z.literal('image'), props: z.object({ src: z.string().min(1), alt: z.string().optional().default(''), }), }) const buttonObject = baseObject.extend({ type: z.literal('button'), props: z.object({ label: z.string().min(1), href: z.string().optional().default(''), color: colorHex.optional(), background: colorHex.optional(), }), }) const inputObject = baseObject.extend({ type: z.literal('input'), props: z.object({ name: z.string().min(1), label: z.string().min(1), placeholder: z.string().optional().default(''), required: z.boolean().optional().default(false), pattern: z.string().optional(), minLength: z.number().int().nonnegative().optional(), maxLength: z.number().int().nonnegative().optional(), help: z.string().optional(), }), }) const selectObject = baseObject.extend({ type: z.literal('select'), props: z.object({ name: z.string().min(1), label: z.string().min(1), options: z.array(z.string()).default([]), defaultValue: z.string().optional().default(''), help: z.string().optional(), }), }) const textareaObject = baseObject.extend({ type: z.literal('textarea'), props: z.object({ name: z.string().min(1), label: z.string().min(1), placeholder: z.string().optional().default(''), rows: z.number().int().positive().default(3), required: z.boolean().optional().default(false), minLength: z.number().int().nonnegative().optional(), maxLength: z.number().int().nonnegative().optional(), help: z.string().optional(), }), }) const radioObject = baseObject.extend({ type: z.literal('radio'), props: z.object({ name: z.string().min(1), legend: z.string().min(1), options: z.array(z.string()).default([]), defaultValue: z.string().optional().default(''), required: z.boolean().optional().default(false), help: z.string().optional(), }), }) const checkboxObject = baseObject.extend({ type: z.literal('checkbox'), props: z.object({ name: z.string().min(1), legend: z.string().min(1), options: z.array(z.string()).default([]), defaultValues: z.array(z.string()).default([]), required: z.boolean().optional().default(false), help: z.string().optional(), }), }) export const objectSchema = z.discriminatedUnion('type', [textObject, imageObject, buttonObject, inputObject, selectObject, textareaObject, radioObject, checkboxObject]) export type WObject = z.infer export const layerSchema = z.object({ id: z.string().min(1), name: z.string().min(1), visible: z.boolean(), locked: z.boolean(), objects: z.array(objectSchema), }) export const frameSchema = z.object({ id: z.string().min(1), width: z.number().positive(), height: z.number().positive(), background: colorHex, layers: z.array(layerSchema), }) export type Frame = z.infer