import { z } from 'zod'; export enum PPTX_SHAPE_TYPE { TEXT_BOX = 'text_box', PICTURE_BOX = 'picture_box', ICON_BOX = 'icon_box', CUSTOM_SHAPE = 'custom_shape', TABLE = 'table', CHEVRON = 'chevron', } export enum PPTX_CHEVRON_DIRECTION { RIGHT = 'right', LEFT = 'left', UP = 'up', DOWN = 'down', } export enum PPTX_ARROW_TYPE { RIGHT = 'rightArrow', LEFT = 'leftArrow', UP = 'upArrow', DOWN = 'downArrow', } export enum PPTX_H_ALIGN { LEFT = 'left', CENTER = 'center', RIGHT = 'right', JUSTIFY = 'justify', } export enum PPTX_V_ALIGN { TOP = 'top', MIDDLE = 'middle', BOTTOM = 'bottom', } export enum PPTX_OBJECT_FIT { COVER = 'cover', CONTAIN = 'contain', CROP = 'crop', } export enum PPTX_BORDER_TYPE { SOLID = 'solid', NONE = 'none', DASH = 'dash', } export enum PPTX_FILL_TYPE { SOLID = 'solid', NONE = 'none', } export enum PPTX_SHADOW_TYPE { OUTER = 'outer', INNER = 'inner', NONE = 'none', } export enum PPTX_CUSTOM_SHAPE_TYPE { ROUND_RECT = 'roundRect', } export const PPTXShadowSchema = z.object({ type: z.nativeEnum(PPTX_SHADOW_TYPE), opacity: z.number().min(0).max(100).optional().describe('percents, range 0-100'), color: z.string().optional().describe('hex color'), blur: z.number().min(0).max(100).optional().describe('degrees, range 0-100'), angle: z.number().min(0).max(359).optional().describe('degrees, range 0-359'), offset: z.number().min(0).max(200).optional().describe('points, range 0-200'), rotateWithShape: z.boolean().optional().describe('whether or not to rotate shadow with shape'), }); export const PPTXFillSchema = z.object({ color: z.string(), type: z.nativeEnum(PPTX_FILL_TYPE), transparency: z.number().min(0).max(100).optional().describe('percents, range 0-100'), alpha: z.number().min(0).max(1).optional().describe('range 0-1'), }); export const PPTXMarginSchema = z.union([ z.number(), z.tuple([z.number(), z.number(), z.number(), z.number()]), ]); export type PPTXMargin = z.infer; // Common export const PPTXPositionSchema = z.object({ height: z.number(), width: z.number(), top: z.number(), left: z.number(), }); export type PPTXPosition = z.infer; export const PPTXFontSchema = z.object({ bold: z.boolean().optional(), color: z.string(), name: z.string(), size: z.number(), lineHeight: z .union([ z.object({ percent: z.number(), }), z.object({ px: z.number(), }), ]) .optional(), }); export type PPTXFont = z.infer; // Textual export const PPTXParagraphSchema = z.object({ alignment: z.nativeEnum(PPTX_H_ALIGN), font: PPTXFontSchema, valign: z.nativeEnum(PPTX_V_ALIGN).optional(), margin: PPTXMarginSchema.optional(), rotate: z.number().optional(), text: z.string(), }); export type PPTXParagraph = z.infer; export const PPTXTextBoxSchema = z.object({ type: z.literal(PPTX_SHAPE_TYPE.TEXT_BOX), position: PPTXPositionSchema, paragraphs: z.array(PPTXParagraphSchema), }); export type PPTXTextBox = z.infer; // Picture export const PPTXPictureBoxSchema = z.object({ type: z.literal(PPTX_SHAPE_TYPE.PICTURE_BOX), position: PPTXPositionSchema, borderRadius: z.array(z.number()).optional(), objectFit: z.nativeEnum(PPTX_OBJECT_FIT), data: z.union([ z.object({ base64: z .string() .describe('Base64 encoded image in "image/{mimeType};base64,{bas64}" format'), }), z.object({ path: z.string().describe('URL to image'), }), ]), }); export type PPTXPictureBox = z.infer; export const PPTXIconBoxSchema = z.object({ type: z.literal(PPTX_SHAPE_TYPE.ICON_BOX), position: PPTXPositionSchema, data: z.union([ z.object({ path: z.string().describe('URL to SVG icon'), color: z .string() .optional() .describe('Optional icon color in hex format (#RRGGBB or #RGB)'), }), z.object({ svg: z.string().describe('Raw SVG string. Backend can optionally apply color override'), color: z .string() .optional() .describe('Optional icon color in hex format (#RRGGBB or #RGB)'), }), z.object({ base64: z .string() .describe('Base64 encoded image in "image/{mimeType};base64,{bas64}" format'), }), ]), }); export type PPTXIconBox = z.infer; // CustomShape export const PPTXCustomShapeSchema = z.object({ type: z.literal(PPTX_SHAPE_TYPE.CUSTOM_SHAPE), customShapeType: z.nativeEnum(PPTX_CUSTOM_SHAPE_TYPE).optional(), position: PPTXPositionSchema, borderRadius: z.number(), fill: PPTXFillSchema.optional(), shadow: PPTXShadowSchema.optional(), }); export type PPTXCustomShape = z.infer; // Table export const PPTXTableCellSchema = z.object({ text: z.string().optional(), font: PPTXFontSchema.optional(), fill: z .object({ color: z.string(), type: z.nativeEnum(PPTX_FILL_TYPE), transparency: z.number().min(0).max(100).optional().describe('percents, range 0-100'), }) .optional(), align: z.nativeEnum(PPTX_H_ALIGN).optional(), valign: z.nativeEnum(PPTX_V_ALIGN).optional(), colspan: z.number().int().optional(), rowspan: z.number().int().optional(), margin: PPTXMarginSchema.optional(), }); export type PPTXTableCell = z.infer; export const PPTXTableRowSchema = z.array(PPTXTableCellSchema); export type PPTXTableRow = z.infer; export const PPTXTableShapeSchema = z.object({ type: z.literal(PPTX_SHAPE_TYPE.TABLE), position: PPTXPositionSchema, rows: z.array(PPTXTableRowSchema), columnWidths: z.array(z.number()).optional(), headerRows: z.number().int().min(0).optional(), border: z.object({ type: z.nativeEnum(PPTX_BORDER_TYPE), color: z.string(), px: z.number(), }), }); export type PPTXTableShape = z.infer; // Chevron export const PPTXChevronShapeSchema = z.object({ type: z.literal(PPTX_SHAPE_TYPE.CHEVRON), direction: z.nativeEnum(PPTX_CHEVRON_DIRECTION).default(PPTX_CHEVRON_DIRECTION.RIGHT), position: PPTXPositionSchema, fill: PPTXFillSchema.optional(), text: z.string().optional(), font: PPTXFontSchema.optional(), align: z .nativeEnum(PPTX_H_ALIGN) .optional() .default('center' as PPTX_H_ALIGN), valign: z .nativeEnum(PPTX_V_ALIGN) .optional() .default('middle' as PPTX_V_ALIGN), }); export type PPTXChevronShape = z.infer; // Overview export const PPTXShapeSchema = z.discriminatedUnion('type', [ PPTXTextBoxSchema, PPTXPictureBoxSchema, PPTXIconBoxSchema, PPTXCustomShapeSchema, PPTXTableShapeSchema, PPTXChevronShapeSchema, ]); export type PPTXShape = z.infer; export const SlideBackground = z.union([ PPTXFillSchema, z.object({ path: z.string(), }), ]); export type SlideBackground = z.infer; export const PPTXSlideSchema = z.object({ background: SlideBackground.optional(), shapes: z.array(PPTXShapeSchema), }); export type PPTXSlide = z.infer; export const PPTXExportPayloadSchema = z.object({ slides: z.array(PPTXSlideSchema), }); export type PPTXExportPayload = z.infer;